2019-01-05 20:43:48 +01:00
import logging
2023-11-15 20:26:05 +01:00
import sys
2022-09-09 18:38:08 +02:00
from pathlib import Path
2019-07-27 21:08:19 +02:00
import requests
2023-11-14 18:33:23 +01:00
from cbpi import __version__ , __codename__
2022-02-19 12:33:11 +01:00
from cbpi . configFolder import ConfigFolder
2019-08-16 21:36:55 +02:00
from cbpi . utils . utils import load_config
2021-01-30 22:29:33 +01:00
from zipfile import ZipFile
2019-01-05 20:43:48 +01:00
from cbpi . craftbeerpi import CraftBeerPi
2024-07-06 09:06:48 +02:00
import os
try :
import pwd
module_pwd = True
except :
module_pwd = False
2022-02-26 21:45:10 +01:00
import pkgutil
2019-01-05 20:43:48 +01:00
import shutil
2021-01-30 22:29:33 +01:00
import click
2022-09-12 21:54:51 +02:00
import pathlib
2021-02-06 14:11:30 +01:00
from subprocess import call
2022-02-26 21:45:10 +01:00
from colorama import Fore , Back , Style
import importlib
2022-02-19 12:33:11 +01:00
from importlib_metadata import metadata
2022-02-26 21:45:10 +01:00
from tabulate import tabulate
2023-02-07 17:30:36 +01:00
import inquirer
2022-04-26 07:19:25 +02:00
import platform
2022-09-24 21:55:36 +02:00
import time
2019-07-27 21:08:19 +02:00
2022-02-20 11:50:44 +01:00
class CraftBeerPiCli ( ) :
def __init__ ( self , config ) - > None :
self . config = config
pass
def setup ( self ) :
print ( " Setting up CraftBeerPi " )
self . config . create_home_folder_structure ( )
self . config . create_config_file ( )
def start ( self ) :
if self . config . check_for_setup ( ) is False :
return
print ( " START " )
cbpi = CraftBeerPi ( self . config )
cbpi . start ( )
def setup_one_wire ( self ) :
print ( " Setting up 1Wire " )
2023-10-19 07:14:22 +02:00
with open ( ' /boot/config.txt ' , ' r ' ) as f :
lines = f . readlines ( )
2023-10-19 07:24:47 +02:00
lines . append ( " dtoverlay=w1-gpio,gpiopin=4,pullup=on " )
2023-10-19 07:38:15 +02:00
configtempfile = os . path . join ( self . config . get_file_path ( " " ) , " config.txt " )
with open ( configtempfile , ' w ' ) as f :
2023-10-19 07:33:37 +02:00
for line in lines :
f . write ( line )
2023-10-19 07:38:15 +02:00
destfile = " /boot/config.txt "
2023-10-19 07:45:05 +02:00
#copy and remove afterwards as mv will work, but raise an error message due to different file owners
2023-10-19 07:41:30 +02:00
shutil . os . system ( ' sudo cp " {} " " {} " ' . format ( configtempfile , destfile ) )
2023-10-19 07:45:05 +02:00
shutil . os . system ( ' rm -rf " {} " ' . format ( configtempfile ) )
2023-10-19 07:30:59 +02:00
2022-02-20 11:50:44 +01:00
print ( " /boot/config.txt created " )
def list_one_wire ( self ) :
print ( " List 1Wire " )
2023-10-14 16:17:03 +02:00
call ( [ " sudo " , " modprobe " , " w1-gpio " ] )
call ( [ " sudo " , " modprobe " , " w1-therm " ] )
2021-07-04 11:23:19 +02:00
try :
2022-02-20 11:50:44 +01:00
for dirname in os . listdir ( ' /sys/bus/w1/devices ' ) :
if ( dirname . startswith ( " 28 " ) or dirname . startswith ( " 10 " ) ) :
print ( dirname )
2021-07-04 11:23:19 +02:00
except Exception as e :
print ( e )
2022-02-20 11:50:44 +01:00
def plugins_list ( self ) :
result = [ ]
print ( " " )
print ( Fore . LIGHTYELLOW_EX , " List of active plugins " , Style . RESET_ALL )
print ( " " )
discovered_plugins = {
name : importlib . import_module ( name )
for finder , name , ispkg
in pkgutil . iter_modules ( )
if name . startswith ( ' cbpi ' ) and len ( name ) > 4
2022-02-26 21:45:10 +01:00
}
2022-02-20 11:50:44 +01:00
for key , module in discovered_plugins . items ( ) :
try :
meta = metadata ( key )
result . append ( dict ( Name = meta [ " Name " ] , Version = meta [ " Version " ] , Author = meta [ " Author " ] , Homepage = meta [ " Home-page " ] , Summary = meta [ " Summary " ] ) )
except Exception as e :
print ( e )
print ( Fore . LIGHTGREEN_EX , tabulate ( result , headers = " keys " ) , Style . RESET_ALL )
2023-02-07 17:30:36 +01:00
def plugin_create ( self , pluginName ) :
2022-02-20 11:50:44 +01:00
print ( " Plugin Creation " )
print ( " " )
2023-02-07 17:30:36 +01:00
questions = [ inquirer . Text ( ' name ' , message = ' Plugin Name (will be prefixed with " cbpi4- " ) ' , default = pluginName ) , ]
2022-02-20 11:50:44 +01:00
2023-02-07 17:30:36 +01:00
answers = inquirer . prompt ( questions )
if answers [ " name " ] == " " :
print ( " you failed to provide a name for the new plugin - terminating " )
return
2022-02-20 11:50:44 +01:00
2022-09-16 10:25:31 +02:00
name = " cbpi4- " + str ( answers [ " name " ] ) . replace ( ' _ ' , ' - ' ) . replace ( ' ' , ' - ' )
2022-02-20 11:50:44 +01:00
if os . path . exists ( os . path . join ( " . " , name ) ) is True :
print ( " Cant create Plugin. Folder {} already exists " . format ( name ) )
return
2021-02-01 01:38:04 +01:00
2023-05-31 16:52:02 +02:00
url = ' https://github.com/PiBrewing/craftbeerpi4-plugin-template/archive/main.zip '
2022-02-20 11:50:44 +01:00
r = requests . get ( url )
with open ( ' temp.zip ' , ' wb ' ) as f :
f . write ( r . content )
with ZipFile ( ' temp.zip ' , ' r ' ) as repo_zip :
repo_zip . extractall ( )
2022-09-24 21:55:36 +02:00
time . sleep ( 1 ) # windows dev container permissions problem otherwise
2022-02-20 11:50:44 +01:00
2022-09-24 21:55:36 +02:00
os . rename ( os . path . join ( " . " , " craftbeerpi4-plugin-template-main " ) , os . path . join ( " . " , name ) )
2022-02-20 11:50:44 +01:00
os . rename ( os . path . join ( " . " , name , " src " ) , os . path . join ( " . " , name , name ) )
import jinja2
templateLoader = jinja2 . FileSystemLoader ( searchpath = os . path . join ( " . " , name ) )
templateEnv = jinja2 . Environment ( loader = templateLoader )
TEMPLATE_FILE = " setup.py "
template = templateEnv . get_template ( TEMPLATE_FILE )
outputText = template . render ( name = name )
with open ( os . path . join ( " . " , name , " setup.py " ) , " w " ) as fh :
fh . write ( outputText )
TEMPLATE_FILE = " MANIFEST.in "
template = templateEnv . get_template ( TEMPLATE_FILE )
outputText = template . render ( name = name )
with open ( os . path . join ( " . " , name , " MANIFEST.in " ) , " w " ) as fh :
fh . write ( outputText )
TEMPLATE_FILE = os . path . join ( " / " , name , " config.yaml " )
operatingsystem = str ( platform . system ( ) ) . lower ( )
if operatingsystem . startswith ( " win " ) :
TEMPLATE_FILE = str ( TEMPLATE_FILE ) . replace ( ' \\ ' , ' / ' )
template = templateEnv . get_template ( TEMPLATE_FILE )
outputText = template . render ( name = name )
with open ( os . path . join ( " . " , name , name , " config.yaml " ) , " w " ) as fh :
fh . write ( outputText )
print ( " " )
print ( " " )
print ( " Plugin {} {} {} created! " . format ( Fore . LIGHTGREEN_EX , name , Style . RESET_ALL ) )
print ( " " )
print ( " Developer Documentation: https://openbrewing.gitbook.io/craftbeerpi4_support/readme/development " )
print ( " " )
print ( " Happy developing! Cheers " )
print ( " " )
print ( " " )
def autostart ( self , name ) :
''' Enable or disable autostart '''
if ( name == " status " ) :
if os . path . exists ( os . path . join ( " /etc/systemd/system " , " craftbeerpi.service " ) ) is True :
print ( " CraftBeerPi Autostart is {} ON {} " . format ( Fore . LIGHTGREEN_EX , Style . RESET_ALL ) )
else :
print ( " CraftBeerPi Autostart is {} OFF {} " . format ( Fore . RED , Style . RESET_ALL ) )
elif ( name == " on " ) :
2024-07-02 21:17:34 +02:00
#user=os.getlogin()
user = pwd . getpwuid ( os . getuid ( ) ) . pw_name
2023-10-18 20:33:21 +02:00
path = " /usr/local/bin/cbpi "
2023-10-18 20:30:26 +02:00
if os . path . exists ( " /home/ " + user + " /.local/bin/cbpi " ) is True :
2023-10-18 20:33:21 +02:00
path = " /home/ " + user + " /.local/bin/cbpi "
2022-02-20 11:50:44 +01:00
print ( " Add craftbeerpi.service to systemd " )
try :
if os . path . exists ( os . path . join ( " /etc/systemd/system " , " craftbeerpi.service " ) ) is False :
2023-10-15 09:59:05 +02:00
templatefile = self . config . get_file_path ( " craftbeerpi.template " )
shutil . os . system ( ' cp " {} " " {} " ' . format ( templatefile , self . config . get_file_path ( " craftbeerpi.service " ) ) )
2022-02-20 11:50:44 +01:00
srcfile = self . config . get_file_path ( " craftbeerpi.service " )
2023-10-15 09:59:05 +02:00
import jinja2
templateLoader = jinja2 . FileSystemLoader ( searchpath = os . path . join ( self . config . get_file_path ( " " ) ) )
templateEnv = jinja2 . Environment ( loader = templateLoader )
operatingsystem = str ( platform . system ( ) ) . lower ( )
if operatingsystem . startswith ( " win " ) :
srcfile = str ( srcfile ) . replace ( ' \\ ' , ' / ' )
template = templateEnv . get_template ( " craftbeerpi.service " )
2023-10-18 20:30:26 +02:00
outputText = template . render ( user = user , path = path )
2023-10-15 09:59:05 +02:00
with open ( srcfile , " w " ) as fh :
fh . write ( outputText )
2022-02-20 11:50:44 +01:00
destfile = os . path . join ( " /etc/systemd/system " )
2023-10-15 09:59:05 +02:00
shutil . os . system ( ' sudo mv " {} " " {} " ' . format ( srcfile , destfile ) )
2022-02-20 11:50:44 +01:00
print ( " Copied craftbeerpi.service to /etc/systemd/system " )
2023-10-14 16:39:00 +02:00
shutil . os . system ( ' sudo systemctl enable craftbeerpi.service ' )
2022-02-20 11:50:44 +01:00
print ( ' Enabled craftbeerpi service ' )
2023-10-14 16:39:00 +02:00
shutil . os . system ( ' sudo systemctl start craftbeerpi.service ' )
2022-02-20 11:50:44 +01:00
print ( ' Started craftbeerpi.service ' )
else :
print ( " craftbeerpi.service is already located in /etc/systemd/system " )
except Exception as e :
print ( e )
return
return
2023-10-14 16:17:03 +02:00
elif ( name == " off " ) :
2022-02-20 11:50:44 +01:00
print ( " Remove craftbeerpi.service from systemd " )
try :
status = os . popen ( ' systemctl list-units --type=service --state=running | grep craftbeerpi.service ' ) . read ( )
if status . find ( " craftbeerpi.service " ) != - 1 :
2023-10-14 16:39:00 +02:00
shutil . os . system ( ' sudo systemctl stop craftbeerpi.service ' )
2022-02-20 11:50:44 +01:00
print ( ' Stopped craftbeerpi service ' )
2023-10-14 16:39:00 +02:00
shutil . os . system ( ' sudo systemctl disable craftbeerpi.service ' )
2022-02-20 11:50:44 +01:00
print ( ' Removed craftbeerpi.service as service ' )
else :
print ( ' craftbeerpi.service service is not running ' )
if os . path . exists ( os . path . join ( " /etc/systemd/system " , " craftbeerpi.service " ) ) is True :
2023-10-14 16:17:03 +02:00
shutil . os . system ( ' sudo rm -rf " {} " ' . format ( os . path . join ( " /etc/systemd/system " , " craftbeerpi.service " ) ) )
2022-02-20 11:50:44 +01:00
print ( " Deleted craftbeerpi.service from /etc/systemd/system " )
else :
print ( " craftbeerpi.service is not located in /etc/systemd/system " )
except Exception as e :
print ( e )
return
return
def chromium ( self , name ) :
''' Enable or disable autostart '''
if ( name == " status " ) :
if os . path . exists ( os . path . join ( " /etc/xdg/autostart/ " , " chromium.desktop " ) ) is True :
print ( " CraftBeerPi Chromium Desktop is {} ON {} " . format ( Fore . LIGHTGREEN_EX , Style . RESET_ALL ) )
else :
print ( " CraftBeerPi Chromium Desktop is {} OFF {} " . format ( Fore . RED , Style . RESET_ALL ) )
elif ( name == " on " ) :
print ( " Add chromium.desktop to /etc/xdg/autostart/ " )
try :
if os . path . exists ( os . path . join ( " /etc/xdg/autostart/ " , " chromium.desktop " ) ) is False :
srcfile = self . config . get_file_path ( " chromium.desktop " )
destfile = os . path . join ( " /etc/xdg/autostart/ " )
2023-10-14 16:33:41 +02:00
shutil . os . system ( ' sudo cp " {} " " {} " ' . format ( srcfile , destfile ) )
2022-02-20 11:50:44 +01:00
print ( " Copied chromium.desktop to /etc/xdg/autostart/ " )
else :
print ( " chromium.desktop is already located in /etc/xdg/autostart/ " )
except Exception as e :
print ( e )
return
return
elif ( name == " off " ) :
print ( " Remove chromium.desktop from /etc/xdg/autostart/ " )
try :
if os . path . exists ( os . path . join ( " /etc/xdg/autostart/ " , " chromium.desktop " ) ) is True :
2023-10-14 16:33:41 +02:00
shutil . os . system ( ' sudo rm -rf " {} " ' . format ( os . path . join ( " /etc/xdg/autostart/ " , " chromium.desktop " ) ) )
2022-02-20 11:50:44 +01:00
print ( " Deleted chromium.desktop from /etc/xdg/autostart/ " )
else :
print ( " chromium.desktop is not located in /etc/xdg/autostart/ " )
except Exception as e :
print ( e )
return
return
2019-01-21 22:33:29 +01:00
2019-08-16 21:36:55 +02:00
2021-01-30 22:29:33 +01:00
@click.group ( )
2022-02-19 12:33:11 +01:00
@click.pass_context
@click.option ( ' --config-folder-path ' , ' -c ' , default = " ./config " , type = click . Path ( ) , help = " Specify where the config folder is located. Defaults to ' ./config ' . " )
2022-09-12 21:54:51 +02:00
@click.option ( ' --logs-folder-path ' , ' -l ' , default = " " , type = click . Path ( ) , help = " Specify where the log folder is located. Defaults to ' ../logs ' relative from the config folder. " )
2023-11-14 18:33:23 +01:00
@click.option ( ' --debug-log-level ' , ' -d ' , default = " 99 " , type = int , help = " Specify the log level you want to write to all logs. 0=ALL, 10=DEBUG, 20=INFO 30(default)=WARNING, 40=ERROR, 50=CRITICAL. Can be also set in config.yaml (debug-log-level: INT) " )
2022-09-12 21:54:51 +02:00
def main ( context , config_folder_path , logs_folder_path , debug_log_level ) :
2023-11-14 18:33:23 +01:00
print ( " -------------------------- " )
print ( " Welcome to CBPi " + __version__ )
print ( " -------------------------- " )
2022-09-12 21:54:51 +02:00
if logs_folder_path == " " :
logs_folder_path = os . path . join ( Path ( config_folder_path ) . absolute ( ) . parent , ' logs ' )
formatter = logging . Formatter ( ' %(asctime)s - %(levelname)s - %(name)s - %(message)s ' )
2023-11-15 20:26:05 +01:00
config = ConfigFolder ( config_folder_path , logs_folder_path )
static_config = load_config ( config . get_file_path ( " config.yaml " ) )
2023-11-14 18:33:23 +01:00
try :
if debug_log_level == 99 :
debug_log_level = static_config [ ' debug-log-level ' ]
except :
debug_log_level = 30
2022-11-27 15:16:18 +01:00
logging . basicConfig ( format = formatter , stream = logging . StreamHandler ( ) )
2022-09-09 18:38:08 +02:00
logger = logging . getLogger ( )
2023-11-14 18:33:23 +01:00
print ( " ******************************* " )
2023-11-15 20:26:05 +01:00
print ( " Debug-log-level is {} " . format ( debug_log_level ) )
2023-11-14 18:33:23 +01:00
print ( " ******************************* " )
2022-11-27 15:16:18 +01:00
logger . setLevel ( debug_log_level )
2022-09-12 21:54:51 +02:00
try :
if not os . path . isdir ( logs_folder_path ) :
logger . info ( f " logs folder ' { logs_folder_path } ' doesnt exist and we are trying to create it " )
pathlib . Path ( logs_folder_path ) . mkdir ( parents = True , exist_ok = True )
logger . info ( f " logs folder ' { logs_folder_path } ' successfully created " )
2024-06-01 19:14:56 +02:00
handler = logging . handlers . RotatingFileHandler ( os . path . join ( logs_folder_path , f " cbpi.log " ) , maxBytes = 1000000 , backupCount = 3 )
logger . addHandler ( handler )
handler . setFormatter ( formatter )
2022-09-12 21:54:51 +02:00
except Exception as e :
logger . warning ( " log folder or log file could not be created or accessed. check folder and file permissions or create the logs folder somewhere you have access with a start option like ' --log-folder-path=./logs ' " )
logging . critical ( e , exc_info = True )
2023-11-15 20:26:05 +01:00
cbpi_cli = CraftBeerPiCli ( config )
2022-02-20 11:50:44 +01:00
context . obj = cbpi_cli
2019-08-16 21:36:55 +02:00
2022-02-19 12:33:11 +01:00
@main.command ( )
@click.pass_context
def setup ( context ) :
2021-01-30 22:29:33 +01:00
''' Create Config folder '''
2022-02-20 11:50:44 +01:00
context . obj . setup ( )
2021-02-01 01:38:04 +01:00
2022-02-19 12:33:11 +01:00
@main.command ( )
2022-02-20 11:50:44 +01:00
@click.pass_context
2021-02-06 14:19:51 +01:00
@click.option ( ' --list ' , is_flag = True , help = " List all 1Wire Devices " )
@click.option ( ' --setup ' , is_flag = True , help = " Setup 1Wire on Raspberry Pi " )
2022-02-20 11:50:44 +01:00
def onewire ( context , list , setup ) :
2023-11-14 18:33:23 +01:00
''' (--setup | --list) Setup 1wire on Raspberry Pi or list sensors '''
2023-11-15 20:26:05 +01:00
operationsystem = sys . platform
if not operationsystem . startswith ( ' win ' ) :
if setup is True :
context . obj . setup_one_wire ( )
if list is True :
context . obj . list_one_wire ( )
else :
print ( " Onewire options NOT available under Windows " )
2021-02-06 14:19:51 +01:00
2022-02-19 12:33:11 +01:00
@main.command ( )
@click.pass_context
def start ( context ) :
2022-02-20 11:50:44 +01:00
context . obj . start ( )
2021-02-01 01:38:04 +01:00
2022-02-19 12:33:11 +01:00
@main.command ( )
@click.pass_context
def plugins ( context ) :
2021-01-30 22:29:33 +01:00
''' List active plugins '''
2022-02-20 11:50:44 +01:00
context . obj . plugins_list ( )
2021-02-01 01:38:04 +01:00
2022-04-26 07:19:25 +02:00
@main.command ( )
2022-02-20 11:50:44 +01:00
@click.pass_context
2023-02-07 17:30:36 +01:00
@click.argument ( ' pluginname ' , nargs = - 1 , required = False )
def create ( context , pluginname = [ ] ) :
2022-02-26 21:45:10 +01:00
''' Create New Plugin '''
2023-02-07 17:30:36 +01:00
sentence = " "
for word in pluginname :
if sentence != " " :
sentence + = " "
sentence + = word
context . obj . plugin_create ( sentence )
2022-02-19 12:33:11 +01:00
@main.command ( )
@click.pass_context
2022-02-20 11:50:44 +01:00
@click.argument ( ' name ' )
2022-02-19 12:33:11 +01:00
def autostart ( context , name ) :
2022-02-28 11:15:02 +01:00
''' (on|off|status) Enable or disable autostart '''
2023-11-15 20:26:05 +01:00
operationsystem = sys . platform
if not operationsystem . startswith ( ' win ' ) :
context . obj . autostart ( name )
else :
print ( " Autostart option NOT available under Windows " )
2021-02-01 01:38:04 +01:00
2019-01-21 22:33:29 +01:00
2022-02-19 12:33:11 +01:00
@main.command ( )
@click.pass_context
2022-02-20 11:50:44 +01:00
@click.argument ( ' name ' )
2022-02-19 12:33:11 +01:00
def chromium ( context , name ) :
2022-02-28 11:15:02 +01:00
''' (on|off|status) Enable or disable Kiosk mode '''
2023-11-15 20:26:05 +01:00
operationsystem = sys . platform
if not operationsystem . startswith ( ' win ' ) :
context . obj . chromium ( name )
else :
print ( " Chromium option NOT available under Windows " )