import logging import sys from pathlib import Path import requests from cbpi import __version__, __codename__ from cbpi.configFolder import ConfigFolder from cbpi.utils.utils import load_config from zipfile import ZipFile from cbpi.craftbeerpi import CraftBeerPi import os try: import pwd module_pwd=True except: module_pwd=False import pkgutil import shutil import click import pathlib from subprocess import call from colorama import Fore, Back, Style import importlib from importlib_metadata import metadata from tabulate import tabulate import inquirer import platform import time 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") with open('/boot/config.txt', 'r') as f: lines=f.readlines() lines.append("dtoverlay=w1-gpio,gpiopin=4,pullup=on") configtempfile=os.path.join(self.config.get_file_path(""),"config.txt") with open(configtempfile, 'w') as f: for line in lines: f.write(line) destfile="/boot/config.txt" #copy and remove afterwards as mv will work, but raise an error message due to different file owners shutil.os.system('sudo cp "{}" "{}"'.format(configtempfile,destfile)) shutil.os.system('rm -rf "{}"'.format(configtempfile)) print("/boot/config.txt created") def list_one_wire(self): print("List 1Wire") call(["sudo","modprobe", "w1-gpio"]) call(["sudo","modprobe", "w1-therm"]) try: for dirname in os.listdir('/sys/bus/w1/devices'): if (dirname.startswith("28") or dirname.startswith("10")): print(dirname) except Exception as e: print(e) 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 } 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) def plugin_create(self, pluginName): print("Plugin Creation") print("") questions = [ inquirer.Text('name', message='Plugin Name (will be prefixed with "cbpi4-")', default=pluginName),] answers = inquirer.prompt(questions) if answers["name"] == "": print("you failed to provide a name for the new plugin - terminating") return name = "cbpi4-" + str(answers["name"]).replace('_', '-').replace(' ', '-') if os.path.exists(os.path.join(".", name)) is True: print("Cant create Plugin. Folder {} already exists ".format(name)) return url = 'https://github.com/PiBrewing/craftbeerpi4-plugin-template/archive/main.zip' 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() time.sleep(1) # windows dev container permissions problem otherwise os.rename(os.path.join(".","craftbeerpi4-plugin-template-main"), os.path.join(".", name)) 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"): #user=os.getlogin() user=pwd.getpwuid(os.getuid()).pw_name path="/usr/local/bin/cbpi" if os.path.exists("/home/"+user+"/.local/bin/cbpi") is True: path="/home/"+user+"/.local/bin/cbpi" print("Add craftbeerpi.service to systemd") try: if os.path.exists(os.path.join("/etc/systemd/system","craftbeerpi.service")) is False: templatefile=self.config.get_file_path("craftbeerpi.template") shutil.os.system('cp "{}" "{}"'.format(templatefile,self.config.get_file_path("craftbeerpi.service"))) srcfile = self.config.get_file_path("craftbeerpi.service") 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") outputText = template.render(user=user, path=path) with open(srcfile, "w") as fh: fh.write(outputText) destfile = os.path.join("/etc/systemd/system") shutil.os.system('sudo mv "{}" "{}"'.format(srcfile,destfile)) print("Copied craftbeerpi.service to /etc/systemd/system") shutil.os.system('sudo systemctl enable craftbeerpi.service') print('Enabled craftbeerpi service') shutil.os.system('sudo systemctl start craftbeerpi.service') print('Started craftbeerpi.service') else: print("craftbeerpi.service is already located in /etc/systemd/system") except Exception as e: print(e) return return elif(name == "off"): 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: shutil.os.system('sudo systemctl stop craftbeerpi.service') print('Stopped craftbeerpi service') shutil.os.system('sudo systemctl disable craftbeerpi.service') 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: shutil.os.system('sudo rm -rf "{}"'.format(os.path.join("/etc/systemd/system","craftbeerpi.service"))) 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/") shutil.os.system('sudo cp "{}" "{}"'.format(srcfile,destfile)) 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: shutil.os.system('sudo rm -rf "{}"'.format(os.path.join("/etc/xdg/autostart/","chromium.desktop"))) 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 @click.group() @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'.") @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.") @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)") def main(context, config_folder_path, logs_folder_path, debug_log_level): print("--------------------------") print("Welcome to CBPi "+__version__) print("--------------------------") 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') config=ConfigFolder(config_folder_path, logs_folder_path) static_config = load_config(config.get_file_path("config.yaml")) try: if debug_log_level == 99: debug_log_level=static_config['debug-log-level'] except: debug_log_level=30 logging.basicConfig(format=formatter, stream=logging.StreamHandler()) logger = logging.getLogger() print("*******************************") print("Debug-log-level is {}".format(debug_log_level)) print("*******************************") logger.setLevel(debug_log_level) 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") handler=logging.handlers.RotatingFileHandler(os.path.join(logs_folder_path, f"cbpi.log"), maxBytes=1000000, backupCount=3) logger.addHandler(handler) handler.setFormatter(formatter) 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) cbpi_cli = CraftBeerPiCli(config) context.obj = cbpi_cli @main.command() @click.pass_context def setup(context): '''Create Config folder''' context.obj.setup() @main.command() @click.pass_context @click.option('--list', is_flag=True, help="List all 1Wire Devices") @click.option('--setup', is_flag=True, help="Setup 1Wire on Raspberry Pi") def onewire(context, list, setup): '''(--setup | --list) Setup 1wire on Raspberry Pi or list sensors''' 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") @main.command() @click.pass_context def start(context): context.obj.start() @main.command() @click.pass_context def plugins(context): '''List active plugins''' context.obj.plugins_list() @main.command() @click.pass_context @click.argument('pluginname', nargs=-1, required=False) def create(context, pluginname=[]): '''Create New Plugin''' sentence = "" for word in pluginname: if sentence != "": sentence += " " sentence += word context.obj.plugin_create(sentence) @main.command() @click.pass_context @click.argument('name') def autostart(context, name): '''(on|off|status) Enable or disable autostart''' operationsystem= sys.platform if not operationsystem.startswith('win'): context.obj.autostart(name) else: print("Autostart option NOT available under Windows") @main.command() @click.pass_context @click.argument('name') def chromium(context, name): '''(on|off|status) Enable or disable Kiosk mode''' operationsystem= sys.platform if not operationsystem.startswith('win'): context.obj.chromium(name) else: print("Chromium option NOT available under Windows")