From ca3bd5950bdd1d8ab0db333f8c1c142b4a60399a Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 17 Nov 2020 19:29:37 +0100 Subject: [PATCH 01/42] Further python3 changes --- code/python/package/README.txt | 17 +++--- .../python/package/pimodules/configuration.py | 4 +- code/python/package/pimodules/daemon.py | 59 +++++++++---------- code/python/upspico/picofssd/QUICKSTART.md | 22 ++++--- code/python/upspico/picofssd/scripts/picofssd | 57 +++++++++++------- .../picofssd/scripts/picofssdxmlconfig | 33 +++++------ 6 files changed, 98 insertions(+), 94 deletions(-) diff --git a/code/python/package/README.txt b/code/python/package/README.txt index e45a6a7..fd0c185 100644 --- a/code/python/package/README.txt +++ b/code/python/package/README.txt @@ -7,25 +7,25 @@ This directory contains the installation scripts and the PiModules(R) Python pac Installation ------------ -First you need to install a couple of Python dependencies. Install python-pip like this: +First you need to install a couple of Python dependencies. Install python3-pip like this: - sudo apt-get install python-pip + sudo apt-get install python3-pip Then install both `jinja2` and `xmltodict` like this: - sudo pip install jinja2 - sudo pip install xmltodict + sudo pip3 install jinja2 + sudo pip3 install xmltodict -Both of the above libraries are used in the sending of email alerts by the file-safe shutdown +Both of the above libraries are used in the sending of email alerts by the file-safe shutdown daemon. Now install the pimodules package: - sudo python setup.py install + sudo python3 setup.py install You can record what files are installed by the above process by running it like this: - sudo python setup.py install --record + sudo python3 setup.py install --record Where will be a text file containing one line per file installed by the process. @@ -68,6 +68,3 @@ This file. * setup.py Installation file, see `README.md` in the directory above this one. - - - diff --git a/code/python/package/pimodules/configuration.py b/code/python/package/pimodules/configuration.py index 9440d23..ecac5fc 100755 --- a/code/python/package/pimodules/configuration.py +++ b/code/python/package/pimodules/configuration.py @@ -41,9 +41,7 @@ def write_config_xml(xmlfile, dict): with open(xmlfile, "wt") as fo: xmltodict.unparse(dict, fo, pretty=True) except IOError as e: - print "Error writing XML file: ", e + print("Error writing XML file: "+e) return False return True - - diff --git a/code/python/package/pimodules/daemon.py b/code/python/package/pimodules/daemon.py index 16193a2..50b4ace 100644 --- a/code/python/package/pimodules/daemon.py +++ b/code/python/package/pimodules/daemon.py @@ -6,14 +6,14 @@ import logging import logging.handlers -from signal import SIGTERM +from signal import SIGTERM class Daemon(object): """ A forking daemon - + Usage: subclass the Daemon class and override the run() method """ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): @@ -29,38 +29,38 @@ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/ self.stderr = stderr self.pidfile = pidfile - + def daemonize(self): """ - do the UNIX double-fork magic, see Stevens' "Advanced + do the UNIX double-fork magic, see Stevens' "Advanced Programming in the UNIX Environment" for details (ISBN 0201563177) http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 """ - try: - pid = os.fork() + try: + pid = os.fork() if pid > 0: # exit first parent - sys.exit(0) - except OSError, e: + sys.exit(0) + except OSError as e: sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) - + # decouple from parent environment - os.chdir("/") - os.setsid() - os.umask(0) - + os.chdir("/") + os.setsid() + os.umask(0) + # do second fork - try: - pid = os.fork() + try: + pid = os.fork() if pid > 0: # exit from second parent - sys.exit(0) - except OSError, e: + sys.exit(0) + except OSError as e: sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) - sys.exit(1) - + sys.exit(1) + # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() @@ -72,12 +72,12 @@ def daemonize(self): os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) - + # write pidfile atexit.register(self.deletePidFile) pid = str(os.getpid()) file(self.pidfile,'w+').write("%s\n" % pid) - + def deletePidFile(self): os.remove(self.pidfile) @@ -97,13 +97,13 @@ def start(self, daemonize=True): pf.close() except IOError: pid = None - + if daemonize: if pid: message = "pidfile %s already exist. Daemon already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) - + # Start the daemon if daemonize: self.daemonize() @@ -122,9 +122,9 @@ def stop(self): pid = int(pf.read().strip()) pf.close() except IOError: - print 'could not get pid' + print('could not get pid') pid = None - + if self.daemon: if not pid: message = "pidfile %s does not exist. Daemon not running?\n" @@ -132,18 +132,18 @@ def stop(self): return # not an error in a restart if self.daemon: - # Try killing the daemon process + # Try killing the daemon process try: while 1: os.kill(pid, SIGTERM) time.sleep(0.1) - except OSError, err: + except OSError as err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: - print str(err) + print(str(err)) sys.exit(1) def restart(self): @@ -160,6 +160,3 @@ def run(self): """ raise NotImplementedError - - - diff --git a/code/python/upspico/picofssd/QUICKSTART.md b/code/python/upspico/picofssd/QUICKSTART.md index 4fd47fa..7dc6732 100644 --- a/code/python/upspico/picofssd/QUICKSTART.md +++ b/code/python/upspico/picofssd/QUICKSTART.md @@ -2,12 +2,12 @@ Installation ------------ -See README.txt in this directory for dependancies. +See README.txt in this directory for dependencies. - sudo python setup.py install + sudo python3 setup.py install cd ../../package - sudo python setup.py install - sudo update-rc.d picofssd defaults + sudo python3 setup.py install + sudo update-rc.d picofssd defaults #TODO change to upstart! sudo update-rc.d picofssd enable After dependancies have been installed and the Pi has been rebooted the daemon should start. @@ -15,8 +15,8 @@ After dependancies have been installed and the Pi has been rebooted the daemon s XML Configuration File ---------------------- -The file-safe shutdown daemon requires an XML configuration file to run. It controls the email -alert settings that the daemon uses to know how to send an email when it detects a shutdown has been +The file-safe shutdown daemon requires an XML configuration file to run. It controls the email +alert settings that the daemon uses to know how to send an email when it detects a shutdown has been requested by the UPS PIco. A script is installed by the installation process which can be used to create this XML file. @@ -43,12 +43,12 @@ Or it can create this file directly like this: sudo picofssdxmlconfig -d -o /etc/pimodules/picofssd/picofssd.xml -You can also read from an existing XML configuration file and create a new one, keeping some of the +You can also read from an existing XML configuration file and create a new one, keeping some of the settings from the old file and only changing your choice of settings. To do this: picofssdxmlconfig -i oldfile.xml -o newfile.xml -Settings will be read from the old file and presented for you to either keep or replace and write +Settings will be read from the old file and presented for you to either keep or replace and write forward to the new file. Here is an explanation of the settings you will be asked to enter. @@ -77,7 +77,7 @@ The email address to which the alert email should be sent. The password required by the email server. -The password is stored on the filesystem encrypted and decryypted by the daemon when it sends an +The password is stored on the filesystem encrypted and decryypted by the daemon when it sends an email. * Security method @@ -94,7 +94,5 @@ A file name of the `jinja2` template used by the daemon to generate the text for Absolute path to a `jinja2` template used by the daemon to generate the email message body. -In most cases you will be asked to confirm each entry and asked to enter each twice to make sure you +In most cases you will be asked to confirm each entry and asked to enter each twice to make sure you entered them correctly. - - diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index a117518..c8bd080 100644 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -1,26 +1,38 @@ -#!/usr/bin/python +#!/usr/bin/python3 +from sys import platform + +if (platform == "linux") or (platform == "linux2"): + pass +else: + # Replace libraries by fake ones + import sys + import fake_rpi + + sys.modules['RPi'] = fake_rpi.RPi # Fake RPi + sys.modules['RPi.GPIO'] = fake_rpi.RPi.GPIO # Fake GPIO + sys.modules['smbus'] = fake_rpi.smbus # Fake smbus (I2C) + +from pimodules import configuration +from pimodules.alerts import sendEmail +from pimodules.daemon import Daemon +import RPi.GPIO as GPIO +import socket +import xmltodict +import argparse +import logging.handlers +import logging +import time +import atexit +import signal +import os """ PiModules(R) UPS PIco file-safe shutdown daemon. """ -import sys -import os -import signal -import atexit -import time -import logging -import logging.handlers -import argparse -import xmltodict -import socket -import RPi.GPIO as GPIO -from pimodules.daemon import Daemon -from pimodules.alerts import sendEmail -from pimodules import configuration CLOCK_PIN = 27 PULSE_PIN = 22 @@ -33,10 +45,17 @@ class fssd(Daemon): self.loglevel = loglevel self.log = logging.getLogger(__name__) self.log.setLevel(self.loglevel) - handler = logging.handlers.SysLogHandler(address = '/dev/log') + self.log.setLevel(logging.DEBUG) + if (platform == "linux") or (platform == "linux2"): + handler = logging.handlers.SysLogHandler(address = '/dev/log') + else: + handler = logging.StreamHandler(sys.stdout) formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') handler.setFormatter(formatter) self.log.addHandler(handler) + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(formatter) + self.log.addHandler(handler) try: self.xmlconfig = xmlconfig @@ -119,7 +138,7 @@ class fssd(Daemon): GPIO.cleanup() def alert_email(self): - #emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate + # emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate try: if not self.config['fssd:enabled']: self.log.debug("Email alert is disabled") @@ -139,7 +158,6 @@ class fssd(Daemon): """ Super-class overloaded run method. """ - self.log.info("Started") self.log.debug(self.config['fssd:enabled']) self.log.debug(self.config['fssd:server']) @@ -165,9 +183,8 @@ group = parser.add_mutually_exclusive_group(required=True) group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", action="store_true", default=False) group.add_argument("-p", "--pid-file", help="PID file") args = parser.parse_args() - + sd = fssd(args.pid_file, args.xml_config, {'info':logging.INFO, 'debug':logging.DEBUG}[args.log_level]) # the argument to the start method is opposite of debug sd.start(not args.debug) - diff --git a/code/python/upspico/picofssd/scripts/picofssdxmlconfig b/code/python/upspico/picofssd/scripts/picofssdxmlconfig index 7a84be4..1ab981d 100755 --- a/code/python/upspico/picofssd/scripts/picofssdxmlconfig +++ b/code/python/upspico/picofssd/scripts/picofssdxmlconfig @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 import argparse import xmltodict @@ -11,12 +11,12 @@ def get_yesno(help, prompt): """ Accepts either 'y' or 'n' at the command-line """ - print help + print(help) yn = None while yn not in ['y','n']: yn = raw_input(prompt) or 'x' if yn not in ['y','n']: - print "Please answer 'y' or 'n'" + print("Please answer 'y' or 'n'") return yn == 'y' @@ -28,7 +28,7 @@ def is_correct(prompt, strval): while yn not in ['y','n']: yn = raw_input(format("You entered %s for the %s, is this correct? " % (strval, prompt))).lower() if yn not in ['y','n']: - print "Please answer 'y' or 'n'" + print("Please answer 'y' or 'n'") return yn == 'y' @@ -37,7 +37,7 @@ def enter_string(help, prompt, default=None, twice=True): Accepts a string value at the command line, asks the user to confirm whether it is the correct value and optionally does the accept twice and compares the two occurrences """ - print help + print(help) default = default or '' savedefault = default while True: @@ -48,7 +48,7 @@ def enter_string(help, prompt, default=None, twice=True): pt = format("Enter the value for the %s: " % prompt) str1 = raw_input(pt) if not str1: - print "You cannot enter a zero-length string" + print("You cannot enter a zero-length string") continue if is_correct(prompt, str1): @@ -68,7 +68,7 @@ def enter_string(help, prompt, default=None, twice=True): if str1 == str2: break else: - print "Values do not match" + print("Values do not match") return str2 @@ -80,7 +80,7 @@ def enter_integer(help, prompt, default=None, twice=True): integer, optionally accepting it twice and comparing the two strings to make sure they are the same """ - print help + print(help) default = default or '' savedefault = default while True: @@ -91,15 +91,15 @@ def enter_integer(help, prompt, default=None, twice=True): pt = format("Enter the value for the %s: " % prompt) str1 = raw_input(pt) if not str1: - print "You cannot enter a zero-length string" + print("You cannot enter a zero-length string") continue try: integer = int(str1) except ValueError: - print "You must enter a valid integer" + print("You must enter a valid integer") continue except TypeError: - print "You must enter a valid integer" + print("You must enter a valid integer") continue if is_correct(prompt, str1): @@ -119,7 +119,7 @@ def enter_integer(help, prompt, default=None, twice=True): if str1 == str2: break else: - print "Values do not match" + print("Values do not match") return str2 @@ -129,7 +129,7 @@ def double_enter_password(help, prompt): Accepts a password entered at the command-line and checks both instances are the same """ - print help + print(help) while True: pt = format("Enter the value for the %s: " % prompt) str1 = getpass.getpass(pt) @@ -139,7 +139,7 @@ def double_enter_password(help, prompt): if str1 == str2: break else: - print "Values do not match" + print("Values do not match") return str2 @@ -149,7 +149,7 @@ def get_choice(help, prompt, choices, show=False): Accepts a string at the command-line. Choice entered must be one of the choices in the list. Optionally shows the choices available in the prompt """ - print help + print(help) choice = '' if show: prompt += '(' + ','.join(choices) + ')' @@ -316,6 +316,3 @@ configsubdict['fssd:body-template'] = body_template configuration.write_config_xml(args.output_xml, configdict) - - - From a4c2061b3db757b38d29d32f72d7ce38c583ceaa Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 17 Nov 2020 19:31:41 +0100 Subject: [PATCH 02/42] Again py3 change --- .../picofssd/scripts/picofssdxmlconfig | 636 +++++++++--------- 1 file changed, 318 insertions(+), 318 deletions(-) diff --git a/code/python/upspico/picofssd/scripts/picofssdxmlconfig b/code/python/upspico/picofssd/scripts/picofssdxmlconfig index 1ab981d..077ed6b 100755 --- a/code/python/upspico/picofssd/scripts/picofssdxmlconfig +++ b/code/python/upspico/picofssd/scripts/picofssdxmlconfig @@ -1,318 +1,318 @@ -#!/usr/bin/python3 - -import argparse -import xmltodict -import getpass -import base64 -from pimodules import configuration - - -def get_yesno(help, prompt): - """ - Accepts either 'y' or 'n' at the command-line - """ - print(help) - yn = None - while yn not in ['y','n']: - yn = raw_input(prompt) or 'x' - if yn not in ['y','n']: - print("Please answer 'y' or 'n'") - - return yn == 'y' - -def is_correct(prompt, strval): - """ - Asks the user whether a value entered is correct - """ - yn = None - while yn not in ['y','n']: - yn = raw_input(format("You entered %s for the %s, is this correct? " % (strval, prompt))).lower() - if yn not in ['y','n']: - print("Please answer 'y' or 'n'") - - return yn == 'y' - -def enter_string(help, prompt, default=None, twice=True): - """ - Accepts a string value at the command line, asks the user to confirm whether it is the correct - value and optionally does the accept twice and compares the two occurrences - """ - print(help) - default = default or '' - savedefault = default - while True: - if default: - pt = format("Enter the value for the %s [ %s ]: " % (prompt, default)) - str1 = raw_input(pt) or default - else: - pt = format("Enter the value for the %s: " % prompt) - str1 = raw_input(pt) - if not str1: - print("You cannot enter a zero-length string") - continue - - if is_correct(prompt, str1): - if not twice: - return str1 - else: - if str1 != default: - default = '' - - if default: - pt = format("Re-enter the value for the %s (%s): " % (prompt, default)) - str2 = raw_input(pt) or default - else: - pt = format("Re-enter the value for the %s: " % prompt) - str2 = raw_input(pt) - - if str1 == str2: - break - else: - print("Values do not match") - - return str2 - - - -def enter_integer(help, prompt, default=None, twice=True): - """ - Accepts a string at the command-line. Checks whether the string is a valid - integer, optionally accepting it twice and comparing the two strings to make sure - they are the same - """ - print(help) - default = default or '' - savedefault = default - while True: - if default: - pt = format("Enter the value for the %s [ %s ]: " % (prompt, default)) - str1 = raw_input(pt) or default - else: - pt = format("Enter the value for the %s: " % prompt) - str1 = raw_input(pt) - if not str1: - print("You cannot enter a zero-length string") - continue - try: - integer = int(str1) - except ValueError: - print("You must enter a valid integer") - continue - except TypeError: - print("You must enter a valid integer") - continue - - if is_correct(prompt, str1): - if not twice: - return str1 - else: - if str1 != default: - default = '' - - if default: - pt = format("Re-enter the value for the %s (%s): " % (prompt, default)) - str2 = raw_input(pt) or default - else: - pt = format("Re-enter the value for the %s: " % prompt) - str2 = raw_input(pt) - - if str1 == str2: - break - else: - print("Values do not match") - - return str2 - - -def double_enter_password(help, prompt): - """ - Accepts a password entered at the command-line and checks both instances - are the same - """ - print(help) - while True: - pt = format("Enter the value for the %s: " % prompt) - str1 = getpass.getpass(pt) - pt = format("Re-enter the value for the %s: " % prompt) - str2 = getpass.getpass(pt) - - if str1 == str2: - break - else: - print("Values do not match") - - return str2 - - -def get_choice(help, prompt, choices, show=False): - """ - Accepts a string at the command-line. Choice entered must be one of the choices in the list. Optionally shows the choices - available in the prompt - """ - print(help) - choice = '' - if show: - prompt += '(' + ','.join(choices) + ')' - - while choice.lower() not in choices: - try: - choice = raw_input(prompt).lower() - except EOFError: - raise KeyboardInterrupt - - return choice - - -#-- main program - -parser = argparse.ArgumentParser() - -group = parser.add_mutually_exclusive_group(required=True) -group.add_argument("-d", "--default", - help="Start from a fresh XML configuration, do not initialize values from existing XML file", - action="store_true", default=False) -group.add_argument("-i", "--input-xml", - help="Read initial configuration values from an existing file") -parser.add_argument("-o", "--output-xml", - help="Output XML configuration file", - default='picofssd.xml', required=True) - -args = parser.parse_args() - -if args.default: - configdict = configuration.read_config_xml(configuration.DEFAULT_FSSD_XML_CONFIG, True) -else: - configdict = configuration.read_config_xml(args.input_xml, False) - - -configsubdict = configdict['root']['fssd:config']['fssd:alerts']['fssd:email'] - -help = """ - -Follow the prompts and enter strings for the XML configuration which drives -the UPS PIco file-safe shutdown daemon. - -Default values read from the previous XML configuration are shown in square brackets. Just press enter -to accept a default value. - -""" - -enabled = get_yesno(help, "Do you want to enable the email alert? ('y', 'n'): ") -configsubdict['fssd:enabled'] = enabled - - -help = """ - -Email server. This is usually something like 'mail.domain.com', where 'domain' reflects the company providing the -email account. - -""" - -server = configsubdict['fssd:server'] -server = enter_string(help, "mail server", default=server,twice=True) -configsubdict['fssd:server'] = server - -help = """ - -Email account user name. This is the user name used to log into the email server. This is usually -the full email address of the account holder. - -""" - -username = configsubdict['fssd:username'] -username = enter_string(help, "email user name (this is usually the full email address)", default=username, twice=True) -configsubdict['fssd:username'] = username - - -help = """ - -Email address of the originator (sender) of the email. This is usually the same as the user name if -the full email address is used as the server's log in string given above. - -""" - -sender = configsubdict['fssd:sender-email-address'] -sender = enter_string(help, "Sender's email address", default=sender, twice=True) -configsubdict['fssd:sender-email-address'] = sender - -help = """ - -The password for the email server and the email account. - -""" - -password = double_enter_password(help, "password") -configsubdict['fssd:sender-password'] = base64.b64encode(password) - - -help = """ - -The recipient's email address to which the daemon should send an email alert. - -""" - -recipient = configsubdict['fssd:recipient-email-address'] -recipient = enter_string(help, "destination email address", default=recipient, twice=True) -configsubdict['fssd:recipient-email-address'] = recipient - - -help = """ - -The security method employed by the email SMTP server. Permitted methods are SSL (secure server layer) -or TLS (transport level security). - -""" - -security = get_choice(help, "Enter server security method ", ['ssl', 'tls'], True) -configsubdict['fssd:security'] = security - -help = """ - -The port number the email server uses. If security was 'SSL', this is probably 465. - -The value entered must be a valid integer. - -""" - -strport = configsubdict['fssd:port'] -try: - port = int(strport) -except ValueError: - strport = '' -except TypeError: - strport = '' -strport = enter_integer(help, "port number", default=strport, twice=True) -configsubdict['fssd:port'] = strport - - -help = """ - -Absolute path to the template used to generate the subject of the email message -sent by the file-safe shutdown daemon to indicate power failure as detected -by the UPS PIco - -""" - -prompt =" absolute path to the email subject template" -subject_template = configsubdict['fssd:subject-template'] -subject_template = enter_string(help, prompt, default=subject_template, twice=False) -configsubdict['fssd:subject-template'] = subject_template - -help = """ - -Absolute path to the template used to generate the body of the email message -sent by the file-safe shutdown daemon to indicate power failure as detected -by the UPS PIco - -""" - -prompt =" absolute path to the email body template" -body_template = configsubdict['fssd:body-template'] -body_template = enter_string(help, prompt, default=body_template, twice=False) -configsubdict['fssd:body-template'] = body_template - - - - - -configuration.write_config_xml(args.output_xml, configdict) +#!/usr/bin/python3 + +import argparse +import xmltodict +import getpass +import base64 +from pimodules import configuration + + +def get_yesno(help, prompt): + """ + Accepts either 'y' or 'n' at the command-line + """ + print(help) + yn = None + while yn not in ['y','n']: + yn = input(prompt) or 'x' + if yn not in ['y','n']: + print("Please answer 'y' or 'n'") + + return yn == 'y' + +def is_correct(prompt, strval): + """ + Asks the user whether a value entered is correct + """ + yn = None + while yn not in ['y','n']: + yn = input(format("You entered %s for the %s, is this correct? " % (strval, prompt))).lower() + if yn not in ['y','n']: + print("Please answer 'y' or 'n'") + + return yn == 'y' + +def enter_string(help, prompt, default=None, twice=True): + """ + Accepts a string value at the command line, asks the user to confirm whether it is the correct + value and optionally does the accept twice and compares the two occurrences + """ + print(help) + default = default or '' + savedefault = default + while True: + if default: + pt = format("Enter the value for the %s [ %s ]: " % (prompt, default)) + str1 = input(pt) or default + else: + pt = format("Enter the value for the %s: " % prompt) + str1 = input(pt) + if not str1: + print("You cannot enter a zero-length string") + continue + + if is_correct(prompt, str1): + if not twice: + return str1 + else: + if str1 != default: + default = '' + + if default: + pt = format("Re-enter the value for the %s (%s): " % (prompt, default)) + str2 = input(pt) or default + else: + pt = format("Re-enter the value for the %s: " % prompt) + str2 = input(pt) + + if str1 == str2: + break + else: + print("Values do not match") + + return str2 + + + +def enter_integer(help, prompt, default=None, twice=True): + """ + Accepts a string at the command-line. Checks whether the string is a valid + integer, optionally accepting it twice and comparing the two strings to make sure + they are the same + """ + print(help) + default = default or '' + savedefault = default + while True: + if default: + pt = format("Enter the value for the %s [ %s ]: " % (prompt, default)) + str1 = input(pt) or default + else: + pt = format("Enter the value for the %s: " % prompt) + str1 = input(pt) + if not str1: + print("You cannot enter a zero-length string") + continue + try: + integer = int(str1) + except ValueError: + print("You must enter a valid integer") + continue + except TypeError: + print("You must enter a valid integer") + continue + + if is_correct(prompt, str1): + if not twice: + return str1 + else: + if str1 != default: + default = '' + + if default: + pt = format("Re-enter the value for the %s (%s): " % (prompt, default)) + str2 = input(pt) or default + else: + pt = format("Re-enter the value for the %s: " % prompt) + str2 = input(pt) + + if str1 == str2: + break + else: + print("Values do not match") + + return str2 + + +def double_enter_password(help, prompt): + """ + Accepts a password entered at the command-line and checks both instances + are the same + """ + print(help) + while True: + pt = format("Enter the value for the %s: " % prompt) + str1 = getpass.getpass(pt) + pt = format("Re-enter the value for the %s: " % prompt) + str2 = getpass.getpass(pt) + + if str1 == str2: + break + else: + print("Values do not match") + + return str2 + + +def get_choice(help, prompt, choices, show=False): + """ + Accepts a string at the command-line. Choice entered must be one of the choices in the list. Optionally shows the choices + available in the prompt + """ + print(help) + choice = '' + if show: + prompt += '(' + ','.join(choices) + ')' + + while choice.lower() not in choices: + try: + choice = input(prompt).lower() + except EOFError: + raise KeyboardInterrupt + + return choice + + +#-- main program + +parser = argparse.ArgumentParser() + +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("-d", "--default", + help="Start from a fresh XML configuration, do not initialize values from existing XML file", + action="store_true", default=False) +group.add_argument("-i", "--input-xml", + help="Read initial configuration values from an existing file") +parser.add_argument("-o", "--output-xml", + help="Output XML configuration file", + default='picofssd.xml', required=True) + +args = parser.parse_args() + +if args.default: + configdict = configuration.read_config_xml(configuration.DEFAULT_FSSD_XML_CONFIG, True) +else: + configdict = configuration.read_config_xml(args.input_xml, False) + + +configsubdict = configdict['root']['fssd:config']['fssd:alerts']['fssd:email'] + +help = """ + +Follow the prompts and enter strings for the XML configuration which drives +the UPS PIco file-safe shutdown daemon. + +Default values read from the previous XML configuration are shown in square brackets. Just press enter +to accept a default value. + +""" + +enabled = get_yesno(help, "Do you want to enable the email alert? ('y', 'n'): ") +configsubdict['fssd:enabled'] = enabled + + +help = """ + +Email server. This is usually something like 'mail.domain.com', where 'domain' reflects the company providing the +email account. + +""" + +server = configsubdict['fssd:server'] +server = enter_string(help, "mail server", default=server,twice=True) +configsubdict['fssd:server'] = server + +help = """ + +Email account user name. This is the user name used to log into the email server. This is usually +the full email address of the account holder. + +""" + +username = configsubdict['fssd:username'] +username = enter_string(help, "email user name (this is usually the full email address)", default=username, twice=True) +configsubdict['fssd:username'] = username + + +help = """ + +Email address of the originator (sender) of the email. This is usually the same as the user name if +the full email address is used as the server's log in string given above. + +""" + +sender = configsubdict['fssd:sender-email-address'] +sender = enter_string(help, "Sender's email address", default=sender, twice=True) +configsubdict['fssd:sender-email-address'] = sender + +help = """ + +The password for the email server and the email account. + +""" + +password = double_enter_password(help, "password") +configsubdict['fssd:sender-password'] = base64.b64encode(password.encode("utf-8")) + + +help = """ + +The recipient's email address to which the daemon should send an email alert. + +""" + +recipient = configsubdict['fssd:recipient-email-address'] +recipient = enter_string(help, "destination email address", default=recipient, twice=True) +configsubdict['fssd:recipient-email-address'] = recipient + + +help = """ + +The security method employed by the email SMTP server. Permitted methods are SSL (secure server layer) +or TLS (transport level security). + +""" + +security = get_choice(help, "Enter server security method ", ['ssl', 'tls'], True) +configsubdict['fssd:security'] = security + +help = """ + +The port number the email server uses. If security was 'SSL', this is probably 465. + +The value entered must be a valid integer. + +""" + +strport = configsubdict['fssd:port'] +try: + port = int(strport) +except ValueError: + strport = '' +except TypeError: + strport = '' +strport = enter_integer(help, "port number", default=strport, twice=True) +configsubdict['fssd:port'] = strport + + +help = """ + +Absolute path to the template used to generate the subject of the email message +sent by the file-safe shutdown daemon to indicate power failure as detected +by the UPS PIco + +""" + +prompt =" absolute path to the email subject template" +subject_template = configsubdict['fssd:subject-template'] +subject_template = enter_string(help, prompt, default=subject_template, twice=False) +configsubdict['fssd:subject-template'] = subject_template + +help = """ + +Absolute path to the template used to generate the body of the email message +sent by the file-safe shutdown daemon to indicate power failure as detected +by the UPS PIco + +""" + +prompt =" absolute path to the email body template" +body_template = configsubdict['fssd:body-template'] +body_template = enter_string(help, prompt, default=body_template, twice=False) +configsubdict['fssd:body-template'] = body_template + + + + + +configuration.write_config_xml(args.output_xml, configdict) From 297a4a3990e15413560d8188fc7be99cf9c29437 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Nov 2020 21:20:42 +0100 Subject: [PATCH 03/42] Further py3 changes --- code/python/package/pimodules/alerts.py | 2 +- code/python/package/pimodules/daemon.py | 37 +++++++++++++------ code/python/upspico/picofssd/scripts/picofssd | 12 ++---- code/shell/config_for_pico.sh | 15 ++------ 4 files changed, 34 insertions(+), 32 deletions(-) mode change 100644 => 100755 code/python/upspico/picofssd/scripts/picofssd diff --git a/code/python/package/pimodules/alerts.py b/code/python/package/pimodules/alerts.py index 7b47015..b68e79e 100755 --- a/code/python/package/pimodules/alerts.py +++ b/code/python/package/pimodules/alerts.py @@ -40,7 +40,7 @@ def sendEmail( emailserver, username, port, security, fromAddr, toAddr, b64Passw except: raise - password = base64.b64decode(b64Password) + password = base64.b64decode(b64Password).decode('utf-8') tVars= { 'now' : now, 'host' : host, 'ipaddress' : ipaddress } diff --git a/code/python/package/pimodules/daemon.py b/code/python/package/pimodules/daemon.py index 50b4ace..5057292 100644 --- a/code/python/package/pimodules/daemon.py +++ b/code/python/package/pimodules/daemon.py @@ -5,6 +5,7 @@ import atexit import logging import logging.handlers +import contextlib from signal import SIGTERM @@ -30,6 +31,25 @@ def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/ self.pidfile = pidfile + + def redirect_stream(self, system_stream, target_stream): + """ Redirect a system stream to a specified file. + :param standard_stream: A file object representing a standard I/O stream. + :param target_stream: The target file object for the redirected stream, or ``None`` to specify the null device. + :return: ``None``. + `system_stream` is a standard system stream such as + ``sys.stdout``. `target_stream` is an open file object that + should replace the corresponding system stream object. + If `target_stream` is ``None``, defaults to opening the + operating system's null device and using its file descriptor. + """ + if target_stream is None: + target_fd = os.open(os.devnull, os.O_RDWR) + else: + target_fd = os.open(target_stream, os.O_RDWR) + system_stream.flush() + os.dup2(target_fd, system_stream.fileno()) + def daemonize(self): """ do the UNIX double-fork magic, see Stevens' "Advanced @@ -62,21 +82,14 @@ def daemonize(self): sys.exit(1) # redirect standard file descriptors - sys.stdout.flush() - sys.stderr.flush() - - si = file(self.stdin, 'r') - so = file(self.stdout, 'a+') - se = file(self.stderr, 'a+', 0) - - os.dup2(si.fileno(), sys.stdin.fileno()) - os.dup2(so.fileno(), sys.stdout.fileno()) - os.dup2(se.fileno(), sys.stderr.fileno()) + self.redirect_stream(sys.stdin, self.stdin) + self.redirect_stream(sys.stdout, self.stdout) + self.redirect_stream(sys.stderr, self.stderr) # write pidfile atexit.register(self.deletePidFile) pid = str(os.getpid()) - file(self.pidfile,'w+').write("%s\n" % pid) + open(self.pidfile,'w+').write("%s\n" % pid) def deletePidFile(self): os.remove(self.pidfile) @@ -92,7 +105,7 @@ def start(self, daemonize=True): if daemonize: # Check for a pidfile to see if the daemon is already running try: - pf = file(self.pidfile,'r') + pf = open(self.pidfile,'r') pid = int(pf.read().strip()) pf.close() except IOError: diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd old mode 100644 new mode 100755 index c8bd080..d0a4073 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -1,12 +1,11 @@ #!/usr/bin/python3 -from sys import platform +import sys -if (platform == "linux") or (platform == "linux2"): +if (sys.platform == "linux") or (sys.platform == "linux2"): pass else: # Replace libraries by fake ones - import sys import fake_rpi sys.modules['RPi'] = fake_rpi.RPi # Fake RPi @@ -46,20 +45,17 @@ class fssd(Daemon): self.log = logging.getLogger(__name__) self.log.setLevel(self.loglevel) self.log.setLevel(logging.DEBUG) - if (platform == "linux") or (platform == "linux2"): + if (sys.platform == "linux") or (sys.platform == "linux2"): handler = logging.handlers.SysLogHandler(address = '/dev/log') else: handler = logging.StreamHandler(sys.stdout) formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') handler.setFormatter(formatter) self.log.addHandler(handler) - handler = logging.StreamHandler(sys.stdout) - handler.setFormatter(formatter) - self.log.addHandler(handler) try: self.xmlconfig = xmlconfig - with open(self.xmlconfig, 'rt') as fi: + with open(self.xmlconfig, 'rb') as fi: self.config = xmltodict.parse(fi) except IOError as e: self.log.warning("Failed to load XML config file, loading defaults. Alerts will be disabled") diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 0b17fb2..848c81f 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -10,13 +10,7 @@ set -e echo '--- update' apt-get update echo '--- install some packages' -apt-get install -y python-dev python-pip python-serial python-smbus python-jinja2 wiringpi - -echo '--- pip install psutil' -pip install psutil - -echo '--- pip install xmltodict' -pip install xmltodict +apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 wiringpi python3-psutil python3-xmltodict python3-rpi.gpio echo '--- save and edit cmdline.txt' cp /boot/cmdline.txt /boot/cmdline.txt.save @@ -26,12 +20,11 @@ echo '--- save and edit config.txt' cp /boot/config.txt /boot/config.txt.save sed -i 's|#dtparam=i2c_arm=on|dtparam=i2c_arm=on|' /boot/config.txt - echo '--- adding line to config.txt' -echo -e "\n\ndtparam=pi3-disable-bt\n\n" >> /boot/config.txt +echo -e "\n\ndtparam=disable-bt\n\n" >> /boot/config.txt -echo '--- adding lines to /etc/modules' -echo -e "\n\ni2c-bcm2708\ni2c-dev\n\n" >> /etc/modules +#echo '--- adding lines to /etc/modules' +#echo -e "\n\ni2c-bcm2708\ni2c-dev\n\n" >> /etc/modules echo '--- disabling hciuart' systemctl disable hciuart From 81729b5fe4499505bdbac9756a62e67e6328ab4f Mon Sep 17 00:00:00 2001 From: asr Date: Sun, 22 Nov 2020 13:33:46 +0100 Subject: [PATCH 04/42] Chasnges for Py3 --- code/shell/config_for_pico.sh | 52 +++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 848c81f..f087af0 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -10,25 +10,61 @@ set -e echo '--- update' apt-get update echo '--- install some packages' -apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 wiringpi python3-psutil python3-xmltodict python3-rpi.gpio +apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 wiringpi + +echo '--- pip install psutil' +pip install psutil + +echo '--- pip install xmltodict' +pip install xmltodict echo '--- save and edit cmdline.txt' cp /boot/cmdline.txt /boot/cmdline.txt.save sed -i 's| console=serial0,115200 console=tty1||' /boot/cmdline.txt echo '--- save and edit config.txt' -cp /boot/config.txt /boot/config.txt.save -sed -i 's|#dtparam=i2c_arm=on|dtparam=i2c_arm=on|' /boot/config.txt +config="/boot/config.txt" +cp $config /boot/config.txt.save -echo '--- adding line to config.txt' -echo -e "\n\ndtparam=disable-bt\n\n" >> /boot/config.txt +if grep -R "dtparam=i2c_arm=on" $config +then + sed -i 's|#dtparam=i2c_arm=on|dtparam=i2c_arm=on|' $config +else + echo 'dtparam=i2c_arm=on' $config +fi + +raspiuart=`cat $config | grep enable_uart` +if [ "$raspiuart" == "#enable_uart=1" ]; then + sed -i "s,$raspiuart,enable_uart=1," $config +elif [ "$raspiuart" == "#enable_uart=0" ]; then + sed -i "s,$raspiuart,enable_uart=1," $config +elif [ "$raspiuart" == "enable_uart=0" ]; then + sed -i "s,$raspiuart,enable_uart=1," $config +else + sh -c "echo 'enable_uart=1' >> $config" +fi + +### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels + rtcmodule=`cat $config | grep dtoverlay=i2c-rtc,ds1307` + if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then + sed -i -e 's/dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config + fi + sleep 1 -#echo '--- adding lines to /etc/modules' -#echo -e "\n\ni2c-bcm2708\ni2c-dev\n\n" >> /etc/modules echo '--- disabling hciuart' systemctl disable hciuart +echo '--- disable hwclock enable ds1307' +apt-get -y remove fake-hwclock +update-rc.d -f fake-hwclock remove +systemctl disable fake-hwclock + +sed ':a;N;$!ba;s/if \[ -e \/run\/systemd\/system \] ; then\n exit 0\nfi/#if \[ -e \/run\/systemd\/system \] ; then\n# exit 0\n#fi/g' /lib/udev/hwclock-set +sed -i -e 's/\/sbin\/hwclock --rtc=$dev --systz --badyear/#\/sbin\/hwclock --rtc=$dev --systz --badyear/g' /lib/udev/hwclock-set +sed -i -e 's/\/sbin\/hwclock --rtc=$dev --systz/#\/sbin\/hwclock --rtc=$dev --systz/g' /lib/udev/hwclock-set +#do later in fabfile +#hwclock -w + echo '--- all done' exit 0 - From a47a8bd0477cede2453378def533d2eab0462940 Mon Sep 17 00:00:00 2001 From: asr Date: Sun, 22 Nov 2020 21:14:51 +0100 Subject: [PATCH 05/42] Convert binary string to ascii --- code/python/package/pimodules/alerts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/package/pimodules/alerts.py b/code/python/package/pimodules/alerts.py index b68e79e..330e3d3 100755 --- a/code/python/package/pimodules/alerts.py +++ b/code/python/package/pimodules/alerts.py @@ -42,7 +42,7 @@ def sendEmail( emailserver, username, port, security, fromAddr, toAddr, b64Passw password = base64.b64decode(b64Password).decode('utf-8') - tVars= { 'now' : now, 'host' : host, 'ipaddress' : ipaddress } + tVars= { 'now' : now, 'host' : host.encode('ascii'), 'ipaddress' : ipaddress } try: templateLoader = jinja2.FileSystemLoader(searchpath="/") From a471588086e0bfc1261038381fd56acd7a62dbda Mon Sep 17 00:00:00 2001 From: asr Date: Sun, 22 Nov 2020 21:15:17 +0100 Subject: [PATCH 06/42] Changes for py3 --- code/shell/config_for_pico.sh | 40 ++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index f087af0..7868f39 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -10,13 +10,13 @@ set -e echo '--- update' apt-get update echo '--- install some packages' -apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 wiringpi +apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 python3-rpi.gpio wiringpi echo '--- pip install psutil' -pip install psutil +pip3 install psutil echo '--- pip install xmltodict' -pip install xmltodict +pip3 install xmltodict echo '--- save and edit cmdline.txt' cp /boot/cmdline.txt /boot/cmdline.txt.save @@ -26,30 +26,36 @@ echo '--- save and edit config.txt' config="/boot/config.txt" cp $config /boot/config.txt.save +echo '--- enable i2c' if grep -R "dtparam=i2c_arm=on" $config then - sed -i 's|#dtparam=i2c_arm=on|dtparam=i2c_arm=on|' $config + sed -i 's|#dtparam=i2c_arm=on|dtparam=i2c_arm=on|' $config else - echo 'dtparam=i2c_arm=on' $config + echo 'dtparam=i2c_arm=on' $config fi +echo '--- enable uart' raspiuart=`cat $config | grep enable_uart` if [ "$raspiuart" == "#enable_uart=1" ]; then - sed -i "s,$raspiuart,enable_uart=1," $config + sed -i "s,$raspiuart,enable_uart=1," $config elif [ "$raspiuart" == "#enable_uart=0" ]; then - sed -i "s,$raspiuart,enable_uart=1," $config + sed -i "s,$raspiuart,enable_uart=1," $config elif [ "$raspiuart" == "enable_uart=0" ]; then - sed -i "s,$raspiuart,enable_uart=1," $config + sed -i "s,$raspiuart,enable_uart=1," $config else - sh -c "echo 'enable_uart=1' >> $config" + sh -c "echo 'enable_uart=1' >> $config" fi +echo '--- enable rtc' ### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels - rtcmodule=`cat $config | grep dtoverlay=i2c-rtc,ds1307` - if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then - sed -i -e 's/dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config - fi - sleep 1 +#rtcmodule=`cat $config | grep dtoverlay=i2c-rtc,ds1307` +rtcmodule="" +if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then + sed -i -e 's/#dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config +else + sh -c "echo 'dtoverlay=i2c-rtc,ds1307' >> $config" +fi +sleep 1 echo '--- disabling hciuart' @@ -60,9 +66,9 @@ apt-get -y remove fake-hwclock update-rc.d -f fake-hwclock remove systemctl disable fake-hwclock -sed ':a;N;$!ba;s/if \[ -e \/run\/systemd\/system \] ; then\n exit 0\nfi/#if \[ -e \/run\/systemd\/system \] ; then\n# exit 0\n#fi/g' /lib/udev/hwclock-set -sed -i -e 's/\/sbin\/hwclock --rtc=$dev --systz --badyear/#\/sbin\/hwclock --rtc=$dev --systz --badyear/g' /lib/udev/hwclock-set -sed -i -e 's/\/sbin\/hwclock --rtc=$dev --systz/#\/sbin\/hwclock --rtc=$dev --systz/g' /lib/udev/hwclock-set +sed -i ':a;N;$!ba;s/if \[ -e \/run\/systemd\/system \] ; then\n exit 0\nfi/#if \[ -e \/run\/systemd\/system \] ; then\n# exit 0\n#fi/g' /lib/udev/hwclock-set +sed -i -e 's/ \/sbin\/hwclock --rtc=$dev --systz --badyear/ #\/sbin\/hwclock --rtc=$dev --systz --badyear/g' /lib/udev/hwclock-set +sed -i -e 's/ \/sbin\/hwclock --rtc=$dev --systz/ #\/sbin\/hwclock --rtc=$dev --systz/g' /lib/udev/hwclock-set #do later in fabfile #hwclock -w From a5612e21e50c4550fc4274d889b106d470ae0987 Mon Sep 17 00:00:00 2001 From: asr Date: Sun, 22 Nov 2020 21:15:34 +0100 Subject: [PATCH 07/42] Changes for py3 --- pico_status/pico_status_hv3.0.py | 500 ++++++++++++++++++++----------- 1 file changed, 333 insertions(+), 167 deletions(-) diff --git a/pico_status/pico_status_hv3.0.py b/pico_status/pico_status_hv3.0.py index 9e7be5d..87387a3 100644 --- a/pico_status/pico_status_hv3.0.py +++ b/pico_status/pico_status_hv3.0.py @@ -1,10 +1,25 @@ #!/usr/bin/python ##################################################################################### -# pico_status_hv3.0.py -# updated: 20-01-2017 +# pico_status.py +# author : Kyriakos Naziris +# updated: 1-12-2017 # Script to show you some statistics pulled from your UPS PIco HV3.0A +# -*- coding: utf-8 -*- +# improved and completed by PiModules Version 1.0 29.08.2015 +# picoStatus-v3.py by KTB is based on upisStatus.py by Kyriakos Naziris +# Kyriakos Naziris / University of Portsmouth / kyriakos@naziris.co.uk +# +# As per 09-01-2017 +# Improved and modified for PiModules PIco HV3.0A Stack Plus / Plus / Top +# by Siewert Lameijer aka Siewert308SW +# added Watchdog timer functions by asr +##################################################################################### + +# You can install psutil using: sudo pip install psutil +# import psutil + ##################################################################################### # SETTINGS ##################################################################################### @@ -14,205 +29,356 @@ # F = Fahrenheit degrees = "C" +# Do you have a PIco FAN kit installed? +# True or False +fankit = False + +# Do you have a to92 temp sensor installed? +# True or False +to92 = False + +# Do you have extended power? +# True or False +extpwr = False + ##################################################################################### -# It's not necessary to edit anything below this line unless your knowing what to do! +# It's not necessary to edit anything below, unless you're knowing what to do! ##################################################################################### +# pip install filelock + +import os import smbus import time import datetime -i2c = smbus.SMBus(1) + +from filelock import Timeout, FileLock + +i2c = smbus.SMBus(1) # 0 = /dev/i2c-0 (port I2C0), 1 = /dev/i2c-1 (port I2C1) +lock = FileLock('/tmp/i2c.lock', timeout=-1) # evtl SoftFileLock verwenden + def fw_version(): - time.sleep(0.1) - data = i2c.read_byte_data(0x69, 0x26) - data = format(data,"02x") - return data + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x26) + data = format(data, "02x") + return data + def boot_version(): - time.sleep(0.1) - data = i2c.read_byte_data(0x69, 0x25) - data = format(data,"02x") - return data + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x25) + data = format(data, "02x") + return data + def pcb_version(): - time.sleep(0.1) - data = i2c.read_byte_data(0x69, 0x24) - data = format(data,"02x") - return data + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x24) + data = format(data, "02x") + return data + def pwr_mode(): - data = i2c.read_byte_data(0x69, 0x00) - data = data & ~(1 << 7) - if (data == 1): - return "RPi POWERED" - elif (data == 2): - return "BAT POWERED" - else: - return "ERROR" + with lock: + data = i2c.read_byte_data(0x69, 0x00) + data = data & ~(1 << 7) + if (data == 1): + return "RPi POWERED" + elif (data == 2): + return "BAT POWERED" + else: + return "ERROR" + def bat_version(): - time.sleep(0.1) - data = i2c.read_byte_data(0x6b, 0x07) - if (data == 0x46): - return "LiFePO4 (ASCII : F)" - elif (data == 0x51): - return "LiFePO4 (ASCII : Q)" - elif (data == 0x53): - return "LiPO (ASCII: S)" - elif (data == 0x50): - return "LiPO (ASCII: P)" - else: - return "ERROR" + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x6b, 0x07) + if (data == 0x46): + return "LiFePO4 (ASCII : F)" + elif (data == 0x51): + return "LiFePO4 (ASCII : Q)" + elif (data == 0x53): + return "LiPO (ASCII: S)" + elif (data == 0x50): + return "LiPO (ASCII: P)" + else: + return "ERROR" + def bat_runtime(): - time.sleep(0.1) - data = i2c.read_byte_data(0x6b, 0x01) + 1 - if (data == 0x100): - return "TIMER DISABLED" - elif (data == 0xff): - return "TIMER DISABLED" - else: - data = str(data)+ " MIN" - return data + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x6b, 0x01) + 1 + if (data == 0x100): + return "TIMER DISABLED" + elif (data == 0xff): + return "TIMER DISABLED" + else: + data = str(data) + " MIN" + return data + def bat_level(): - time.sleep(0.1) - data = i2c.read_word_data(0x69, 0x08) - data = format(data,"02x") - return (float(data) / 100) + time.sleep(0.1) + with lock: + data = i2c.read_word_data(0x69, 0x08) + data = format(data, "02x") + return (float(data) / 100) + def bat_percentage(): - time.sleep(0.1) - datavolts = bat_level() - databattery = bat_version() - if (databattery == "LiFePO4 (ASCII : F)") or (databattery == "LiFePO4 (ASCII : Q)"): - datapercentage = ((datavolts-2.9)/1.25)*100 + time.sleep(0.1) + datavolts = bat_level() + databattery = bat_version() + if (databattery == "LiFePO4 (ASCII : F)") or (databattery == "LiFePO4 (ASCII : Q)"): + databatminus = datavolts-2.90 + datapercentage = ((databatminus/0.70))*100 + elif (databattery == "LiPO (ASCII: S)") or (databattery == "LiPO (ASCII: P)"): + databatminus = datavolts-3.4 + datapercentage = ((databatminus/0.899))*100 + return int(datapercentage) + + +def charger_state(): + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x20) + battpercentage = bat_percentage() + powermode = pwr_mode() + databattery = bat_version() + if (databattery == "LiFePO4 (ASCII : F)") or (databattery == "LiFePO4 (ASCII : Q)"): + if (data == 0x00) and (powermode == "BAT POWERED"): + return "DISCHARGING" + if (data == 0x01) and (powermode == "RPi POWERED"): + return "CHARGING" + if (data == 0x00) and (powermode == "RPi POWERED"): + return "CHARGED" + if (databattery == "LiPO (ASCII: S)") or (databattery == "LiPO (ASCII: P)"): + if (data == 0x00) and (powermode == "BAT POWERED"): + return "DISCHARGING" + if (data == 0x00) and (powermode == "RPi POWERED"): + return "CHARGED" + if (data == 0x01) and (powermode == "RPi POWERED"): + return "CHARGING" - elif (databattery == "LiPO (ASCII: S)") or (databattery == "LiPO (ASCII: P)"): - datapercentage = ((datavolts-3.4)/0.75)*100 - return datapercentage def rpi_level(): - time.sleep(0.1) - data = i2c.read_word_data(0x69, 0x0a) - data = format(data,"02x") - return (float(data) / 100) + time.sleep(0.1) + with lock: + data = i2c.read_word_data(0x69, 0x0a) + data = format(data, "02x") + powermode = pwr_mode() + if (powermode == "RPi POWERED"): + return (float(data) / 100) + else: + return "0.0" + + +def rpi_cpu_temp(): + time.sleep(0.1) + with lock: + data = os.popen('vcgencmd measure_temp').readline() + data = (data.replace("temp=", "").replace("'C\n", "")) + if (degrees == "C"): + return data + elif (degrees == "F"): + return (float(data) * 9 / 5) + 32 + def ntc1_temp(): - time.sleep(0.1) - data = i2c.read_byte_data(0x69, 0x1b) - data = format(data,"02x") - if (degrees == "C"): - return data - elif (degrees == "F"): - return (float(data) * 9 / 5) + 32 + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x1b) + data = format(data, "02x") + if (degrees == "C"): + return data + elif (degrees == "F"): + return (float(data) * 9 / 5) + 32 + def to92_temp(): - time.sleep(0.1) - data = i2c.read_byte_data(0x69, 0x1C) - data = format(data,"02x") - if (degrees == "C"): - return data - elif (degrees == "F"): - return (float(data) * 9 / 5) + 32 + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x1C) + data = format(data, "02x") + if (degrees == "C"): + return data + elif (degrees == "F"): + return (float(data) * 9 / 5) + 32 + def epr_read(): - time.sleep(0.1) - data = i2c.read_word_data(0x69, 0x0c) - data = format(data,"02x") - return (float(data) / 100) + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x0c) + data = format(data, "02x") + return (float(data) / 100) + def ad2_read(): - time.sleep(0.1) - data = i2c.read_word_data(0x69, 0x14) - data = format(data,"02x") - return (float(data) / 100) - + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x69, 0x07) + data = format(data, "02x") + return (float(data) / 100) + + def fan_mode(): - time.sleep(0.1) - data = i2c.read_byte_data(0x6b, 0x11) - data = data & ~(1 << 2) - if (data == 1): - return "ENABLED" - elif (data == 0): - return "DISABLED" - else: - return "ERROR" - -def fan_state(): - time.sleep(0.1) - data = i2c.read_byte_data(0x6b, 0x13) - data = data & ~(1 << 2) - if (data == 1): - return "ON" - elif (data == 0): - return "OFF" - else: - return "ERROR" + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x6b, 0x11) + data = data & ~(1 << 2) + if (data == 2): + return "AUTOMATIC" + elif (data == 1): + return "ON" + elif (data == 0): + return "OFF" + else: + return "ERROR" + def fan_speed(): - time.sleep(0.1) - data = i2c.read_word_data(0x6b, 0x12) - data = format(data,"02x") - return (float(data) * 10) - -def r232_state(): - time.sleep(0.1) - data = i2c.read_byte_data(0x6b, 0x02) - if (data == 0x00): - return "OFF" - elif (data == 0x01): - return "ON @ 4800 pbs" - elif (data == 0x02): - return "ON @ 9600 pbs" - elif (data == 0x03): - return "ON @ 19200 pbs" - elif (data == 0x04): - return "ON @ 34600 pbs" - elif (data == 0x05): - return "ON @ 57600 pbs" - elif (data == 0x0f): - return "ON @ 115200 pbs" - else: - return "ERROR" - -print " " -print "***********************************" -print " UPS PIco HV3.0A Status " -print "***********************************" -print " " -print " ","UPS PIco Firmware.....:",fw_version() -print " ","UPS PIco Bootloader...:",boot_version() -print " ","UPS PIco PCB Version..:",pcb_version() -print " ","UPS PIco BAT Version..:",bat_version() -print " ","UPS PIco BAT Runtime..:",bat_runtime() -print " ","UPS PIco r232 State...:",r232_state() -print " " -print " ","Powering Mode.........:",pwr_mode() -print " ","BAT Percentage........:",bat_percentage(),"%" -print " ","BAT Voltage...........:",bat_level(),"V" -print " ","RPi Voltage...........:",rpi_level(),"V" - -if (degrees == "C"): - print " ","NTC1 Temperature......:",ntc1_temp(),"C" - print " ","TO-92 Temperature.....:",to92_temp(),"C" -elif (degrees == "F"): - print " ","NTC1 Temperature......:",ntc1_temp(),"F" - print " ","TO-92 Temperature.....:",to92_temp(),"F" -else: - print " ","NTC1 Temperature......: please set your desired temperature symbol!" - print " ","TO-92 Temperature.....: please set your desired temperature symbol!" - -print " ","Extended Voltage......:",epr_read(),"V" -print " ","A/D2 Voltage..........:",ad2_read(),"V" -print " " -print " ","PIco FAN Mode.........:",fan_mode() -print " ","PIco FAN State........:",fan_state() -print " ","PIco FAN Speed........:",fan_speed(),"RPM" -print " " -print "***********************************" -print " Powered by PiCo " -print "***********************************" -print " " \ No newline at end of file + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x6b, 0x12) + data = format(data, "02x") + return int(float(data) * 100) + + +def fan_threshold(): + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x6B, 0x14) + data = format(data, "02x") + if (degrees == "C"): + return data + elif (degrees == "F"): + return (float(data) * 9 / 5) + 32 + + +def rs232_state(): + time.sleep(0.1) + with lock: + data = i2c.read_byte_data(0x6b, 0x02) + if (data == 0x00): + return "OFF" + elif (data == 0xff): + return "OFF" + elif (data == 0x01): + return "ON @ 4800 pbs" + elif (data == 0x02): + return "ON @ 9600 pbs" + elif (data == 0x03): + return "ON @ 19200 pbs" + elif (data == 0x04): + return "ON @ 34600 pbs" + elif (data == 0x05): + return "ON @ 57600 pbs" + elif (data == 0x0f): + return "ON @ 115200 pbs" + else: + return "ERROR" + + +def reset(): + start_watchdog_timer(0) + + +def start_watchdog_timer(seconds): + seconds = int(seconds) + if seconds > 0xFE: + seconds = 0xFE + with lock: + i2c.write_byte_data(0x6b, 0x05, seconds) + + +def stop_watchdog_timer(): + with lock: + i2c.write_byte_data(0x6b, 0x05, 0xFF) + + +def read_watchdog_timer(): + out = -1 + with lock: + out = i2c.read_byte_data(0x6b, 0x05) + return out + + +if __name__ == '__main__': + print(" ") + print("**********************************************") + print("* UPS PIco HV3.0A Status *") + print("* Version 6.0 *") + print("**********************************************") + print(" ") + print(" ", "- PIco Firmware..........:", fw_version()) + print(" ", "- PIco Bootloader........:", boot_version()) + print(" ", "- PIco PCB Version.......:", pcb_version()) + print(" ", "- PIco BAT Version.......:", bat_version()) + print(" ", "- PIco BAT Runtime.......:", bat_runtime()) + print(" ", "- PIco rs232 State.......:", rs232_state()) + print(" ") + print(" ", "- Powering Mode..........:", pwr_mode()) + print(" ", "- Charger State..........:", charger_state()) + print(" ", "- Battery Percentage.....:", bat_percentage(), "%") + print(" ", "- Battery Voltage........:", bat_level(), "V") + print(" ", "- RPi Voltage............:", rpi_level(), "V") + print(" ") + + if (degrees == "C"): + print(" ", "- RPi CPU Temperature....:", rpi_cpu_temp(), "C") + print(" ", "- NTC1 Temperature.......:", ntc1_temp(), "C") + elif (degrees == "F"): + print(" ", "- RPi CPU Temperature....:", rpi_cpu_temp(), "F") + print(" ", "- NTC1 Temperature.......:", ntc1_temp(), "F") + else: + print(" ", "- RPi CPU Temperature....: please set temperature symbol in the script!") + print(" ", "- NTC1 Temperature.......: please set temperature symbol in the script!") + + if (to92 == True): + if (degrees == "C"): + print(" ", "- TO-92 Temperature......:", to92_temp(), "C") + elif (degrees == "F"): + print(" ", "- TO-92 Temperature......:", to92_temp(), "F") + else: + print(" ", "- TO-92 Temperature......: please set temperature symbol in the script!") + + if (extpwr == True): + print(" ", "- Extended Voltage.......:", epr_read(), "V") + print(" ", "- A/D2 Voltage...........:", ad2_read(), "V") + + if (fankit == True): + print(" ") + if (fan_mode() == "AUTOMATIC"): + print(" ", "- PIco FAN Mode..........:", fan_mode()) + if (degrees == "C"): + print(" ", "- PIco FAN Temp Threshold:", fan_threshold(), "C") + elif (degrees == "F"): + print(" ", "- PIco FAN Temp Threshold:", fan_threshold(), "F") + else: + print(" ", "- PIco FAN Temp Threshold: please set temperature symbol in the script!") + else: + print(" ", "- PIco FAN Mode..........:", fan_mode()) + if (fan_mode() == "ON"): + print(" ", "- PIco FAN Speed.........:", fan_speed(), "RPM") + else: + print(" ", "- PIco FAN Speed.........: 0 RPM") + + if (degrees == "C"): + print(" ", "- PIco FAN Temp Threshold:", fan_threshold(), "C") + elif (degrees == "F"): + print(" ", "- PIco FAN Temp Threshold:", fan_threshold(), "F") + else: + print(" ", "- PIco FAN Temp Threshold: please set temperature symbol in the script!") + print(" ") + print("**********************************************") + print("* Powered by PiModules *") + print("**********************************************") + print(" ") From 41c02fb6a4d68eb28dadea826e9b44ab0cfe67e5 Mon Sep 17 00:00:00 2001 From: asr Date: Sun, 22 Nov 2020 23:10:49 +0100 Subject: [PATCH 08/42] enablerd rts --- code/shell/config_for_pico.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 7868f39..f5eba0b 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -48,8 +48,7 @@ fi echo '--- enable rtc' ### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels -#rtcmodule=`cat $config | grep dtoverlay=i2c-rtc,ds1307` -rtcmodule="" +rtcmodule=`cat $config | grep dtoverlay=i2c-rtc,ds1307` if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then sed -i -e 's/#dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config else From 21bfd13ca234ac105768928e8f6bdd9caa3495f1 Mon Sep 17 00:00:00 2001 From: asr Date: Sun, 22 Nov 2020 23:18:46 +0100 Subject: [PATCH 09/42] searching --- code/shell/config_for_pico.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index f5eba0b..1ae827d 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -35,7 +35,7 @@ else fi echo '--- enable uart' -raspiuart=`cat $config | grep enable_uart` +raspiuart=`cat $config | grep enable_uart=` if [ "$raspiuart" == "#enable_uart=1" ]; then sed -i "s,$raspiuart,enable_uart=1," $config elif [ "$raspiuart" == "#enable_uart=0" ]; then From 137e9c153615afbb977e1b09e5a88f435e467d2f Mon Sep 17 00:00:00 2001 From: asr Date: Mon, 23 Nov 2020 16:49:43 +0100 Subject: [PATCH 10/42] searching --- code/shell/config_for_pico.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 1ae827d..d7f335e 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -35,7 +35,7 @@ else fi echo '--- enable uart' -raspiuart=`cat $config | grep enable_uart=` +raspiuart=`cat $config | grep "enable_uart"` if [ "$raspiuart" == "#enable_uart=1" ]; then sed -i "s,$raspiuart,enable_uart=1," $config elif [ "$raspiuart" == "#enable_uart=0" ]; then @@ -48,7 +48,7 @@ fi echo '--- enable rtc' ### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels -rtcmodule=`cat $config | grep dtoverlay=i2c-rtc,ds1307` +rtcmodule=`cat $config | grep "dtoverlay=i2c-rtc,ds1307"` if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then sed -i -e 's/#dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config else From dd8c510fbfdf06b9c8ab14fd43d9cb967b4c6d6d Mon Sep 17 00:00:00 2001 From: asr Date: Mon, 23 Nov 2020 16:50:53 +0100 Subject: [PATCH 11/42] searching --- code/shell/config_for_pico.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index d7f335e..d402599 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -35,7 +35,7 @@ else fi echo '--- enable uart' -raspiuart=`cat $config | grep "enable_uart"` +raspiuart=$(cat $config | grep "enable_uart") if [ "$raspiuart" == "#enable_uart=1" ]; then sed -i "s,$raspiuart,enable_uart=1," $config elif [ "$raspiuart" == "#enable_uart=0" ]; then @@ -48,7 +48,7 @@ fi echo '--- enable rtc' ### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels -rtcmodule=`cat $config | grep "dtoverlay=i2c-rtc,ds1307"` +rtcmodule=$(cat $config | grep "dtoverlay=i2c-rtc,ds1307") if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then sed -i -e 's/#dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config else From 06da7ddcd060bd322d5878eb9d831a7774b8c653 Mon Sep 17 00:00:00 2001 From: asr Date: Mon, 23 Nov 2020 19:50:19 +0100 Subject: [PATCH 12/42] searching --- code/shell/config_for_pico.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index d402599..4bcf77f 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -35,7 +35,7 @@ else fi echo '--- enable uart' -raspiuart=$(cat $config | grep "enable_uart") +raspiuart=`cat $config | grep -R "enable_uart"` if [ "$raspiuart" == "#enable_uart=1" ]; then sed -i "s,$raspiuart,enable_uart=1," $config elif [ "$raspiuart" == "#enable_uart=0" ]; then @@ -48,7 +48,7 @@ fi echo '--- enable rtc' ### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels -rtcmodule=$(cat $config | grep "dtoverlay=i2c-rtc,ds1307") +rtcmodule=`cat $config | grep -R "dtoverlay=i2c-rtc,ds1307"`` if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then sed -i -e 's/#dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config else From 0411459ac9f6ff0856c3af7f7b5826c740053a28 Mon Sep 17 00:00:00 2001 From: asr Date: Mon, 23 Nov 2020 22:44:39 +0100 Subject: [PATCH 13/42] searching --- code/shell/config_for_pico.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 4bcf77f..99757f8 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -48,7 +48,7 @@ fi echo '--- enable rtc' ### Checking if rtc dtoverlay module is loaded which doesn't work on older kernels -rtcmodule=`cat $config | grep -R "dtoverlay=i2c-rtc,ds1307"`` +rtcmodule=`cat $config | grep -R "dtoverlay=i2c-rtc,ds1307"` if [ "$rtcmodule" == "#dtoverlay=i2c-rtc,ds1307" ]; then sed -i -e 's/#dtoverlay=i2c-rtc,ds1307/dtoverlay=i2c-rtc,ds1307/g' $config else From f6d168a310cd441b27092003855aa9a61afe0c96 Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 24 Nov 2020 13:40:17 +0100 Subject: [PATCH 14/42] QuickFix for encode error --- code/python/package/pimodules/alerts.py | 144 ++++++++++++------------ 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/code/python/package/pimodules/alerts.py b/code/python/package/pimodules/alerts.py index 330e3d3..afc84f5 100755 --- a/code/python/package/pimodules/alerts.py +++ b/code/python/package/pimodules/alerts.py @@ -8,78 +8,78 @@ def get_host_name(): - try: - command = "uname -n" - process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) - output = process.communicate()[0] - return output - except: - return None + try: + command = "uname -n" + process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) + output = process.communicate()[0] + return output + except: + return None def get_ip_address(): - try: - command = "hostname -I" - process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) - output = process.communicate()[0] - return output - except: - return None - - -def sendEmail( emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate): - try: - port = int(port) - except ValueError: - port = 0 - - try: - now = datetime.datetime.now() - host = get_host_name() - ipaddress = get_ip_address() - except: - raise - - password = base64.b64decode(b64Password).decode('utf-8') - - tVars= { 'now' : now, 'host' : host.encode('ascii'), 'ipaddress' : ipaddress } - - try: - templateLoader = jinja2.FileSystemLoader(searchpath="/") - templateEnv = jinja2.Environment( loader=templateLoader ) - - subjectTemplate = templateEnv.get_template(msgSubjectTemplate) - bodyTemplate = templateEnv.get_template(msgBodyTemplate) - - msgSubject = subjectTemplate.render(tVars) - msgBody = bodyTemplate.render(tVars) - - except: - raise - - # Writing the message - msg = MIMEText(msgBody) - msg['Subject'] = msgSubject - msg['From'] = fromAddr - msg['To'] = toAddr - - # Sending the message - try: - if port: - serverstring = format("%s:%d" % (emailserver, port)) - else: - serverstring = emailserver - - if security.lower() in ['ssl']: - server = smtplib.SMTP_SSL(serverstring) - else: - server = smtplib.SMTP(serverstring) - server.starttls() - - server.login(username, password) - server.sendmail(fromAddr, [toAddr], msg.as_string()) - server.quit() - except: - raise - - return True + try: + command = "hostname -I" + process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) + output = process.communicate()[0] + return output + except: + return None + + +def sendEmail(emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate): + try: + port = int(port) + except ValueError: + port = 0 + + try: + now = datetime.datetime.now() + host = get_host_name() + ipaddress = get_ip_address() + except: + raise + + password = base64.b64decode(b64Password).decode('utf-8') + + tVars = {'now': now, 'host': host, 'ipaddress': ipaddress} + + try: + templateLoader = jinja2.FileSystemLoader(searchpath="/") + templateEnv = jinja2.Environment(loader=templateLoader) + + subjectTemplate = templateEnv.get_template(msgSubjectTemplate) + bodyTemplate = templateEnv.get_template(msgBodyTemplate) + + msgSubject = subjectTemplate.render(tVars) + msgBody = bodyTemplate.render(tVars) + + except: + raise + + # Writing the message + msg = MIMEText(msgBody) + msg['Subject'] = msgSubject + msg['From'] = fromAddr + msg['To'] = toAddr + + # Sending the message + try: + if port: + serverstring = format("%s:%d" % (emailserver, port)) + else: + serverstring = emailserver + + if security.lower() in ['ssl']: + server = smtplib.SMTP_SSL(serverstring) + else: + server = smtplib.SMTP(serverstring) + server.starttls() + + server.login(username, password) + server.sendmail(fromAddr, [toAddr], msg.as_string()) + server.quit() + except: + raise + + return True From c20220747bef91373743cf3322d191ca5c82eadc Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 24 Nov 2020 14:09:40 +0100 Subject: [PATCH 15/42] QuickFix for encode error --- code/python/upspico/picofssd/scripts/picofssd | 274 +++++++++--------- 1 file changed, 138 insertions(+), 136 deletions(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index d0a4073..ac919f5 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -7,7 +7,7 @@ if (sys.platform == "linux") or (sys.platform == "linux2"): else: # Replace libraries by fake ones import fake_rpi - + sys.modules['RPi'] = fake_rpi.RPi # Fake RPi sys.modules['RPi.GPIO'] = fake_rpi.RPi.GPIO # Fake GPIO sys.modules['smbus'] = fake_rpi.smbus # Fake smbus (I2C) @@ -32,155 +32,157 @@ PiModules(R) UPS PIco file-safe shutdown daemon. """ - CLOCK_PIN = 27 PULSE_PIN = 22 BOUNCE_TIME = 30 class fssd(Daemon): - def __init__(self, pidfile, xmlconfig, loglevel=logging.NOTSET): - Daemon.__init__(self, pidfile) - self.loglevel = loglevel - self.log = logging.getLogger(__name__) - self.log.setLevel(self.loglevel) - self.log.setLevel(logging.DEBUG) - if (sys.platform == "linux") or (sys.platform == "linux2"): - handler = logging.handlers.SysLogHandler(address = '/dev/log') - else: - handler = logging.StreamHandler(sys.stdout) - formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') - handler.setFormatter(formatter) - self.log.addHandler(handler) - - try: - self.xmlconfig = xmlconfig - with open(self.xmlconfig, 'rb') as fi: - self.config = xmltodict.parse(fi) - except IOError as e: - self.log.warning("Failed to load XML config file, loading defaults. Alerts will be disabled") - self.config = xmltodict.parse(configuration.DEFAULT_FSSD_XML_CONFIG) - - self.config = self.config['root']['fssd:config']['fssd:alerts']['fssd:email'] - self.config['fssd:enabled'] = (self.config['fssd:enabled'] == 'True') - - signal.signal(signal.SIGTERM, self.sigcatch) - - self.counter = 0 - - # first interrupt on isr pin will start pulse high - self.sqwave = True - - def setup(self): - """ - GPIO initialisation - - Note: This cannot go in the __init__ method because the unix double-fork in the generic daemon code - mucks up the initialisation of the GPIO system. - - So it is called in the over-ridden run method. - """ - - GPIO.setmode(GPIO.BCM) - GPIO.setwarnings(False) - GPIO.setup(CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) - GPIO.add_event_detect(CLOCK_PIN, GPIO.FALLING, callback=self.isr, bouncetime=BOUNCE_TIME) - - def isr(self, channel): - """ - GPIO interrupt service routine - """ - - # This test is here because the user *might* have another HAT plugged in or another circuit that produces a - # falling-edge signal on another GPIO pin. - if channel != CLOCK_PIN: - return - - # we can get the state of a pin with GPIO.input even when it is currently configured as an output - self.sqwave = not GPIO.input(PULSE_PIN) - - # set pulse pin low before changing it to input to look for shutdown signal - GPIO.output(PULSE_PIN, False) - GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - if not GPIO.input(PULSE_PIN): - # pin is low, this is shutdown signal from pico - self.counter += 1 - self.log.warning("Lost power supply, Pi will shutdown") - self.alert_email() - time.sleep(2) - os.system('/sbin/shutdown -h now') - else: - self.counter = 0 - - # change pulse pin back to output with flipped state - GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) - - def sigcatch(self, signum, frame): - """ - Signal handler - """ - - if signum == signal.SIGTERM: - sys.exit(0) - - def cleanup(self): - """ - GPIO cleanup - """ - - self.log.debug("Cleanup") - self.log.info("Stopped") - GPIO.cleanup() - - def alert_email(self): - # emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate - try: - if not self.config['fssd:enabled']: - self.log.debug("Email alert is disabled") - return True - - sendEmail(self.config['fssd:server'], self.config['fssd:username'], self.config['fssd:port'], self.config['fssd:security'], - self.config['fssd:sender-email-address'], self.config['fssd:recipient-email-address'], - self.config['fssd:sender-password'], self.config['fssd:subject-template'], - self.config['fssd:body-template']) - except socket.error as e: - self.log.error(format("Exception in alert_email: %d, %s" % (e.errno, e.strerror))) - except: - self.log.error("Unexpected error in alert_email:", sys.exc_info()[0]) - - - def run(self): - """ - Super-class overloaded run method. - """ - self.log.info("Started") - self.log.debug(self.config['fssd:enabled']) - self.log.debug(self.config['fssd:server']) - self.log.debug(self.config['fssd:username']) - self.log.debug(self.config['fssd:sender-email-address']) - self.log.debug(self.config['fssd:recipient-email-address']) - - # register function to cleanup at exit - atexit.register(self.cleanup) - - self.setup() - - while True: - time.sleep(5) - + def __init__(self, pidfile, xmlconfig, loglevel=logging.NOTSET): + Daemon.__init__(self, pidfile) + self.loglevel = loglevel + self.log = logging.getLogger(__name__) + self.log.setLevel(self.loglevel) + self.log.setLevel(logging.DEBUG) + if (sys.platform == "linux") or (sys.platform == "linux2"): + handler = logging.handlers.SysLogHandler(address='/dev/log') + else: + handler = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') + handler.setFormatter(formatter) + self.log.addHandler(handler) + + try: + self.xmlconfig = xmlconfig + with open(self.xmlconfig, 'rb') as fi: + self.config = xmltodict.parse(fi) + except IOError as e: + self.log.warning( + "Failed to load XML config file, loading defaults. Alerts will be disabled") + self.config = xmltodict.parse(configuration.DEFAULT_FSSD_XML_CONFIG) + + self.config = self.config['root']['fssd:config']['fssd:alerts']['fssd:email'] + self.config['fssd:enabled'] = (self.config['fssd:enabled'] == 'True') + + signal.signal(signal.SIGTERM, self.sigcatch) + + self.counter = 0 + + # first interrupt on isr pin will start pulse high + self.sqwave = True + + def setup(self): + """ + GPIO initialisation + + Note: This cannot go in the __init__ method because the unix double-fork in the generic daemon code + mucks up the initialisation of the GPIO system. + + So it is called in the over-ridden run method. + """ + + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + GPIO.setup(CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) + GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) + GPIO.add_event_detect(CLOCK_PIN, GPIO.FALLING, callback=self.isr, bouncetime=BOUNCE_TIME) + + def isr(self, channel): + """ + GPIO interrupt service routine + """ + + # This test is here because the user *might* have another HAT plugged in or another circuit that produces a + # falling-edge signal on another GPIO pin. + if channel != CLOCK_PIN: + return + + # we can get the state of a pin with GPIO.input even when it is currently configured as an output + self.sqwave = not GPIO.input(PULSE_PIN) + + # set pulse pin low before changing it to input to look for shutdown signal + GPIO.output(PULSE_PIN, False) + GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) + if not GPIO.input(PULSE_PIN): + # pin is low, this is shutdown signal from pico + self.counter += 1 + self.log.warning("Lost power supply, Pi will shutdown") + self.alert_email() + time.sleep(2) + os.system('/sbin/shutdown -h now') + else: + self.counter = 0 + + # change pulse pin back to output with flipped state + GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) + + def sigcatch(self, signum, frame): + """ + Signal handler + """ + + if signum == signal.SIGTERM: + sys.exit(0) + + def cleanup(self): + """ + GPIO cleanup + """ + + self.log.debug("Cleanup") + self.log.info("Stopped") + GPIO.cleanup() + + def alert_email(self): + # emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate + try: + if not self.config['fssd:enabled']: + self.log.debug("Email alert is disabled") + return True + + sendEmail(self.config['fssd:server'].decode('utf-8'), self.config['fssd:username'], self.config['fssd:port'], self.config['fssd:security'], + self.config['fssd:sender-email-address'], self.config['fssd:recipient-email-address'], + self.config['fssd:sender-password'], self.config['fssd:subject-template'], + self.config['fssd:body-template']) + except socket.error as e: + self.log.error(format("Exception in alert_email: %d, %s" % (e.errno, e.strerror))) + except: + self.log.error("Unexpected error in alert_email:", sys.exc_info()[0]) + + def run(self): + """ + Super-class overloaded run method. + """ + self.log.info("Started") + self.log.debug(self.config['fssd:enabled']) + self.log.debug(self.config['fssd:server']) + self.log.debug(self.config['fssd:username']) + self.log.debug(self.config['fssd:sender-email-address']) + self.log.debug(self.config['fssd:recipient-email-address']) + + # register function to cleanup at exit + atexit.register(self.cleanup) + + self.setup() + + while True: + time.sleep(5) # parse the command-line parser = argparse.ArgumentParser() -parser.add_argument('-l', '--log-level', help="Log level, 'info' or 'debug'", default='info', choices=['info', 'debug']) -parser.add_argument("-x", "--xml-config", help="XML config file", default='picofssd.xml', required=True) +parser.add_argument('-l', '--log-level', help="Log level, 'info' or 'debug'", + default='info', choices=['info', 'debug']) +parser.add_argument("-x", "--xml-config", help="XML config file", + default='picofssd.xml', required=True) group = parser.add_mutually_exclusive_group(required=True) -group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", action="store_true", default=False) +group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", + action="store_true", default=False) group.add_argument("-p", "--pid-file", help="PID file") args = parser.parse_args() -sd = fssd(args.pid_file, args.xml_config, {'info':logging.INFO, 'debug':logging.DEBUG}[args.log_level]) +sd = fssd(args.pid_file, args.xml_config, { + 'info': logging.INFO, 'debug': logging.DEBUG}[args.log_level]) # the argument to the start method is opposite of debug sd.start(not args.debug) From 7194fe34bf374b81fbea181686a5d08abc03e510 Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 24 Nov 2020 14:20:50 +0100 Subject: [PATCH 16/42] QuickFix for encode error --- code/python/package/pimodules/alerts.py | 2 +- code/python/upspico/picofssd/scripts/picofssd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/python/package/pimodules/alerts.py b/code/python/package/pimodules/alerts.py index afc84f5..fb85721 100755 --- a/code/python/package/pimodules/alerts.py +++ b/code/python/package/pimodules/alerts.py @@ -35,7 +35,7 @@ def sendEmail(emailserver, username, port, security, fromAddr, toAddr, b64Passwo try: now = datetime.datetime.now() - host = get_host_name() + host = get_host_name().decode('utf-8') ipaddress = get_ip_address() except: raise diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index ac919f5..2848089 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -140,7 +140,7 @@ class fssd(Daemon): self.log.debug("Email alert is disabled") return True - sendEmail(self.config['fssd:server'].decode('utf-8'), self.config['fssd:username'], self.config['fssd:port'], self.config['fssd:security'], + sendEmail(self.config['fssd:server'], self.config['fssd:username'], self.config['fssd:port'], self.config['fssd:security'], self.config['fssd:sender-email-address'], self.config['fssd:recipient-email-address'], self.config['fssd:sender-password'], self.config['fssd:subject-template'], self.config['fssd:body-template']) From 3fd803fa04e03139fe0b9aad8222263ee7dee775 Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 24 Nov 2020 15:25:54 +0100 Subject: [PATCH 17/42] Locked eMail transition --- code/python/upspico/picofssd/scripts/picofssd | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index 2848089..24f715f 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -25,6 +25,7 @@ import time import atexit import signal import os +from threading import RLock """ PiModules(R) UPS PIco file-safe shutdown daemon. @@ -70,6 +71,7 @@ class fssd(Daemon): # first interrupt on isr pin will start pulse high self.sqwave = True + self.lock = threading.RLock() def setup(self): """ @@ -91,30 +93,30 @@ class fssd(Daemon): """ GPIO interrupt service routine """ - - # This test is here because the user *might* have another HAT plugged in or another circuit that produces a - # falling-edge signal on another GPIO pin. - if channel != CLOCK_PIN: - return - - # we can get the state of a pin with GPIO.input even when it is currently configured as an output - self.sqwave = not GPIO.input(PULSE_PIN) - - # set pulse pin low before changing it to input to look for shutdown signal - GPIO.output(PULSE_PIN, False) - GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - if not GPIO.input(PULSE_PIN): - # pin is low, this is shutdown signal from pico - self.counter += 1 - self.log.warning("Lost power supply, Pi will shutdown") - self.alert_email() - time.sleep(2) - os.system('/sbin/shutdown -h now') - else: - self.counter = 0 - - # change pulse pin back to output with flipped state - GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) + with self.lock: + # This test is here because the user *might* have another HAT plugged in or another circuit that produces a + # falling-edge signal on another GPIO pin. + if channel != CLOCK_PIN: + return + + # we can get the state of a pin with GPIO.input even when it is currently configured as an output + self.sqwave = not GPIO.input(PULSE_PIN) + + # set pulse pin low before changing it to input to look for shutdown signal + GPIO.output(PULSE_PIN, False) + GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) + if not GPIO.input(PULSE_PIN): + # pin is low, this is shutdown signal from pico + self.counter += 1 + self.log.warning("Lost power supply, Pi will shutdown") + self.alert_email() + time.sleep(2) + os.system('/sbin/shutdown -h now') + else: + self.counter = 0 + + # change pulse pin back to output with flipped state + GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) def sigcatch(self, signum, frame): """ From 724aa4c386c093280e078f032fb654b3711bd448 Mon Sep 17 00:00:00 2001 From: asr Date: Tue, 24 Nov 2020 15:59:59 +0100 Subject: [PATCH 18/42] Locked eMail transition --- code/python/upspico/picofssd/scripts/picofssd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index 24f715f..538b1df 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -71,7 +71,7 @@ class fssd(Daemon): # first interrupt on isr pin will start pulse high self.sqwave = True - self.lock = threading.RLock() + self.lock = RLock() def setup(self): """ From 6f72e8ee3b8ab1fe0c7ded76c1b10399a2cf17d3 Mon Sep 17 00:00:00 2001 From: asr Date: Fri, 27 Nov 2020 17:09:39 +0100 Subject: [PATCH 19/42] turn off isr before sending mail --- code/python/upspico/picofssd/scripts/picofssd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index 538b1df..2e2a47c 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -25,7 +25,6 @@ import time import atexit import signal import os -from threading import RLock """ PiModules(R) UPS PIco file-safe shutdown daemon. @@ -106,6 +105,8 @@ class fssd(Daemon): GPIO.output(PULSE_PIN, False) GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) if not GPIO.input(PULSE_PIN): + # disable irq to prevent multiple mails + GPIO.remove_event_detect(CLOCK_PIN) # pin is low, this is shutdown signal from pico self.counter += 1 self.log.warning("Lost power supply, Pi will shutdown") From a73ab745773141341902fd05d21f84b8a65526fb Mon Sep 17 00:00:00 2001 From: asr Date: Fri, 27 Nov 2020 17:14:24 +0100 Subject: [PATCH 20/42] turn off isr before sending mail --- code/python/upspico/picofssd/scripts/picofssd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index 2e2a47c..000df9c 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -104,7 +104,7 @@ class fssd(Daemon): # set pulse pin low before changing it to input to look for shutdown signal GPIO.output(PULSE_PIN, False) GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - if not GPIO.input(PULSE_PIN): + if not GPIO.input(PULSE_PIN) & & (counter == 0): # disable irq to prevent multiple mails GPIO.remove_event_detect(CLOCK_PIN) # pin is low, this is shutdown signal from pico From c419c92de26b274056718b60379ed3a7b8d267d4 Mon Sep 17 00:00:00 2001 From: asr Date: Sat, 28 Nov 2020 13:06:10 +0100 Subject: [PATCH 21/42] on mail --- code/python/upspico/picofssd/scripts/picofssd | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index 000df9c..f476629 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -67,10 +67,10 @@ class fssd(Daemon): signal.signal(signal.SIGTERM, self.sigcatch) self.counter = 0 + self.mail_sent = False # first interrupt on isr pin will start pulse high self.sqwave = True - self.lock = RLock() def setup(self): """ @@ -92,32 +92,33 @@ class fssd(Daemon): """ GPIO interrupt service routine """ - with self.lock: - # This test is here because the user *might* have another HAT plugged in or another circuit that produces a - # falling-edge signal on another GPIO pin. - if channel != CLOCK_PIN: - return - - # we can get the state of a pin with GPIO.input even when it is currently configured as an output - self.sqwave = not GPIO.input(PULSE_PIN) - - # set pulse pin low before changing it to input to look for shutdown signal - GPIO.output(PULSE_PIN, False) - GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - if not GPIO.input(PULSE_PIN) & & (counter == 0): - # disable irq to prevent multiple mails - GPIO.remove_event_detect(CLOCK_PIN) - # pin is low, this is shutdown signal from pico - self.counter += 1 - self.log.warning("Lost power supply, Pi will shutdown") + # This test is here because the user *might* have another HAT plugged in or another circuit that produces a + # falling-edge signal on another GPIO pin. + if channel != CLOCK_PIN: + return + + # we can get the state of a pin with GPIO.input even when it is currently configured as an output + self.sqwave = not GPIO.input(PULSE_PIN) + + # set pulse pin low before changing it to input to look for shutdown signal + GPIO.output(PULSE_PIN, False) + GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) + if not GPIO.input(PULSE_PIN) and (counter == 0): + # disable irq to prevent multiple mails + GPIO.remove_event_detect(CLOCK_PIN) + # pin is low, this is shutdown signal from pico + self.counter += 1 + self.log.warning("Lost power supply, Pi will shutdown") + if self.mail_sent == False: + self.mail_sent = True self.alert_email() - time.sleep(2) - os.system('/sbin/shutdown -h now') - else: - self.counter = 0 + time.sleep(2) + os.system('/sbin/shutdown -h now') + else: + self.counter = 0 - # change pulse pin back to output with flipped state - GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) + # change pulse pin back to output with flipped state + GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) def sigcatch(self, signum, frame): """ From f873191013274c43d8a0d4fc08095c59033b4bc3 Mon Sep 17 00:00:00 2001 From: asr Date: Sat, 28 Nov 2020 13:13:25 +0100 Subject: [PATCH 22/42] on mail --- code/python/upspico/picofssd/scripts/picofssd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd index f476629..531eb75 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd @@ -103,7 +103,7 @@ class fssd(Daemon): # set pulse pin low before changing it to input to look for shutdown signal GPIO.output(PULSE_PIN, False) GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - if not GPIO.input(PULSE_PIN) and (counter == 0): + if not GPIO.input(PULSE_PIN): # disable irq to prevent multiple mails GPIO.remove_event_detect(CLOCK_PIN) # pin is low, this is shutdown signal from pico From 7c9298f5ba2371a17c869890617da6d0b0335d8d Mon Sep 17 00:00:00 2001 From: uenz Date: Mon, 29 Nov 2021 15:10:45 +0100 Subject: [PATCH 23/42] Update config_for_pico.sh Removed wiringpi --- code/shell/config_for_pico.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 99757f8..4a29638 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -10,7 +10,7 @@ set -e echo '--- update' apt-get update echo '--- install some packages' -apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 python3-rpi.gpio wiringpi +apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 python3-rpi.gpio # wiringpi echo '--- pip install psutil' pip3 install psutil From 38526a2c1829101d9ce539cff70e9d1333c0539c Mon Sep 17 00:00:00 2001 From: uenz Date: Sun, 19 Feb 2023 02:08:03 +0100 Subject: [PATCH 24/42] Py 3 adjustments --- code/python/package/pimodules/daemon.py | 204 ++++++++------------- code/python/upspico/picofssd/QUICKSTART.md | 5 +- code/python/upspico/picofssd/setup.py | 3 +- 3 files changed, 80 insertions(+), 132 deletions(-) diff --git a/code/python/package/pimodules/daemon.py b/code/python/package/pimodules/daemon.py index 5057292..1202d77 100644 --- a/code/python/package/pimodules/daemon.py +++ b/code/python/package/pimodules/daemon.py @@ -1,73 +1,28 @@ +"""Generic linux daemon base class for python 3.x.""" -import sys -import os -import time -import atexit -import logging -import logging.handlers -import contextlib - -from signal import SIGTERM - - - -class Daemon(object): - """ - A forking daemon - - Usage: subclass the Daemon class and override the run() method - """ - def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): - self.log = logging.getLogger(__name__) - self.log.setLevel(logging.DEBUG) - handler = logging.handlers.SysLogHandler(address = '/dev/log') - formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') - handler.setFormatter(formatter) - self.log.addHandler(handler) - - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - - self.pidfile = pidfile - - - def redirect_stream(self, system_stream, target_stream): - """ Redirect a system stream to a specified file. - :param standard_stream: A file object representing a standard I/O stream. - :param target_stream: The target file object for the redirected stream, or ``None`` to specify the null device. - :return: ``None``. - `system_stream` is a standard system stream such as - ``sys.stdout``. `target_stream` is an open file object that - should replace the corresponding system stream object. - If `target_stream` is ``None``, defaults to opening the - operating system's null device and using its file descriptor. - """ - if target_stream is None: - target_fd = os.open(os.devnull, os.O_RDWR) - else: - target_fd = os.open(target_stream, os.O_RDWR) - system_stream.flush() - os.dup2(target_fd, system_stream.fileno()) +import sys, os, time, atexit, signal + +class daemon: + """A generic daemon class. + + Usage: subclass the daemon class and override the run() method.""" + + def __init__(self, pidfile): self.pidfile = pidfile def daemonize(self): - """ - do the UNIX double-fork magic, see Stevens' "Advanced - Programming in the UNIX Environment" for details (ISBN 0201563177) - http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 - """ + """Deamonize class. UNIX double fork mechanism.""" try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) - except OSError as e: - sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) + except OSError as err: + sys.stderr.write('fork #1 failed: {0}\n'.format(err)) sys.exit(1) # decouple from parent environment - os.chdir("/") + os.chdir('/') os.setsid() os.umask(0) @@ -75,101 +30,92 @@ def daemonize(self): try: pid = os.fork() if pid > 0: + # exit from second parent sys.exit(0) - except OSError as e: - sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) + except OSError as err: + sys.stderr.write('fork #2 failed: {0}\n'.format(err)) sys.exit(1) # redirect standard file descriptors - self.redirect_stream(sys.stdin, self.stdin) - self.redirect_stream(sys.stdout, self.stdout) - self.redirect_stream(sys.stderr, self.stderr) + sys.stdout.flush() + sys.stderr.flush() + si = open(os.devnull, 'r') + so = open(os.devnull, 'a+') + se = open(os.devnull, 'a+') + + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) # write pidfile - atexit.register(self.deletePidFile) + atexit.register(self.delpid) + pid = str(os.getpid()) - open(self.pidfile,'w+').write("%s\n" % pid) + with open(self.pidfile,'w+') as f: + f.write(pid + '\n') - def deletePidFile(self): + def delpid(self): os.remove(self.pidfile) - def start(self, daemonize=True): - """ - Start the daemon - """ - - self.daemon = daemonize + def start(self): + """Start the daemon.""" + # Check for a pidfile to see if the daemon already runs + try: + with open(self.pidfile,'r') as pf: - if daemonize: - # Check for a pidfile to see if the daemon is already running - try: - pf = open(self.pidfile,'r') pid = int(pf.read().strip()) - pf.close() - except IOError: - pid = None - - if daemonize: - if pid: - message = "pidfile %s already exist. Daemon already running?\n" - sys.stderr.write(message % self.pidfile) - sys.exit(1) + except IOError: + pid = None - # Start the daemon - if daemonize: - self.daemonize() + if pid: + message = "pidfile {0} already exist. " + \ + "Daemon already running?\n" + sys.stderr.write(message.format(self.pidfile)) + sys.exit(1) + # Start the daemon + self.daemonize() self.run() def stop(self): - """ - Stop the daemon - """ - - if self.daemon: - # Get the pid from the pidfile - try: - pf = file(self.pidfile,'r') + """Stop the daemon.""" + + # Get the pid from the pidfile + try: + with open(self.pidfile,'r') as pf: pid = int(pf.read().strip()) - pf.close() - except IOError: - print('could not get pid') - pid = None - - if self.daemon: - if not pid: - message = "pidfile %s does not exist. Daemon not running?\n" - sys.stderr.write(message % self.pidfile) - return # not an error in a restart - - if self.daemon: - # Try killing the daemon process - try: - while 1: - os.kill(pid, SIGTERM) - time.sleep(0.1) - except OSError as err: - err = str(err) - if err.find("No such process") > 0: - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - else: - print(str(err)) - sys.exit(1) + except IOError: + pid = None + + if not pid: + message = "pidfile {0} does not exist. " + \ + "Daemon not running?\n" + sys.stderr.write(message.format(self.pidfile)) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, signal.SIGTERM) + time.sleep(0.1) + except OSError as err: + e = str(err.args) + if e.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print (str(err.args)) + sys.exit(1) def restart(self): - """ - Restart the daemon - """ + """Restart the daemon.""" self.stop() self.start() def run(self): - """ - You should override this method when you subclass Daemon. It will be called after the process has been - daemonized by start() or restart(). - """ + """You should override this method when you subclass Daemon. - raise NotImplementedError + It will be called after the process has been daemonized by + start() or restart().""" diff --git a/code/python/upspico/picofssd/QUICKSTART.md b/code/python/upspico/picofssd/QUICKSTART.md index 7dc6732..3cff429 100644 --- a/code/python/upspico/picofssd/QUICKSTART.md +++ b/code/python/upspico/picofssd/QUICKSTART.md @@ -7,8 +7,9 @@ See README.txt in this directory for dependencies. sudo python3 setup.py install cd ../../package sudo python3 setup.py install - sudo update-rc.d picofssd defaults #TODO change to upstart! - sudo update-rc.d picofssd enable + sudo systemctl deamon-reload + sudo systemctl picofssd.service start + sudo systemctl picofssd.service enable After dependancies have been installed and the Pi has been rebooted the daemon should start. diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index 48affce..c3bfc60 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -25,7 +25,8 @@ datafiles=[ ('/etc/pimodules/picofssd', ['etc/pimodules/picofssd/emailAlertBody.template', 'etc/pimodules/picofssd/emailAlertSubject.template', 'etc/pimodules/picofssd/picofssd.xml']), ('/etc/default', ['default/picofssd']), - ('/etc/init.d', ['init.d/picofssd']) +# ('/etc/init.d', ['init.d/picofssd']), + ('/etc/systemd/system', ['systemd/picofssd.service']) ] setup(name='picofssd', From 142fde501c62c46cbfc5211fd8bb7679f9a795fd Mon Sep 17 00:00:00 2001 From: uenz Date: Sun, 19 Feb 2023 02:12:51 +0100 Subject: [PATCH 25/42] Added missing file --- .../upspico/picofssd/systemd/picofssd.service | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 code/python/upspico/picofssd/systemd/picofssd.service diff --git a/code/python/upspico/picofssd/systemd/picofssd.service b/code/python/upspico/picofssd/systemd/picofssd.service new file mode 100755 index 0000000..adef686 --- /dev/null +++ b/code/python/upspico/picofssd/systemd/picofssd.service @@ -0,0 +1,14 @@ +[Unit] +Description=UPS PIco file-safe shutdown daemon + +[Service] +User=root +Restart=always +RestartSec=1s +StandardOutput=null +StandardError=null +ExecStart=/usr/bin/python3 /usr/local/bin/picofssd --log-level info --pid-file /var/run/picofssd.pid --xml-config /etc/pimodules/picofssd/picofssd.xml +TimeoutSec=30 + +[Install] +WantedBy=multi-user.target From 37f21c1b18e226350d59c5cd8ba8f94b40f8bf76 Mon Sep 17 00:00:00 2001 From: uenz Date: Sun, 19 Feb 2023 02:20:03 +0100 Subject: [PATCH 26/42] Update daemon.py --- code/python/package/pimodules/daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/package/pimodules/daemon.py b/code/python/package/pimodules/daemon.py index 1202d77..a158d4e 100644 --- a/code/python/package/pimodules/daemon.py +++ b/code/python/package/pimodules/daemon.py @@ -2,7 +2,7 @@ import sys, os, time, atexit, signal -class daemon: +class Daemon: """A generic daemon class. Usage: subclass the daemon class and override the run() method.""" From 5b3ebad1cb7f3ec78e56431f71f344109900f2aa Mon Sep 17 00:00:00 2001 From: uenz Date: Sun, 19 Feb 2023 02:27:14 +0100 Subject: [PATCH 27/42] Bug fix in daemon.py --- code/python/package/pimodules/daemon.py | 90 +++++++++++++------------ 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/code/python/package/pimodules/daemon.py b/code/python/package/pimodules/daemon.py index 1202d77..3edffc3 100644 --- a/code/python/package/pimodules/daemon.py +++ b/code/python/package/pimodules/daemon.py @@ -58,56 +58,62 @@ def daemonize(self): def delpid(self): os.remove(self.pidfile) - def start(self): - """Start the daemon.""" + def start(self, daemonize=True): + """ + Start the daemon + """ - # Check for a pidfile to see if the daemon already runs - try: - with open(self.pidfile,'r') as pf: + self.daemon = daemonize - pid = int(pf.read().strip()) - except IOError: - pid = None - if pid: - message = "pidfile {0} already exist. " + \ - "Daemon already running?\n" - sys.stderr.write(message.format(self.pidfile)) - sys.exit(1) + if daemonize: + # Check for a pidfile to see if the daemon already runs + try: + with open(self.pidfile,'r') as pf: + + pid = int(pf.read().strip()) + except IOError: + pid = None + + if pid: + message = "pidfile {0} already exist. " + \ + "Daemon already running?\n" + sys.stderr.write(message.format(self.pidfile)) + sys.exit(1) - # Start the daemon - self.daemonize() + # Start the daemon + self.daemonize() self.run() def stop(self): """Stop the daemon.""" - - # Get the pid from the pidfile - try: - with open(self.pidfile,'r') as pf: - pid = int(pf.read().strip()) - except IOError: - pid = None - - if not pid: - message = "pidfile {0} does not exist. " + \ - "Daemon not running?\n" - sys.stderr.write(message.format(self.pidfile)) - return # not an error in a restart - - # Try killing the daemon process - try: - while 1: - os.kill(pid, signal.SIGTERM) - time.sleep(0.1) - except OSError as err: - e = str(err.args) - if e.find("No such process") > 0: - if os.path.exists(self.pidfile): - os.remove(self.pidfile) - else: - print (str(err.args)) - sys.exit(1) + if self.daemon: + # Get the pid from the pidfile + try: + with open(self.pidfile,'r') as pf: + pid = int(pf.read().strip()) + except IOError: + pid = None + + if not pid: + message = "pidfile {0} does not exist. " + \ + "Daemon not running?\n" + sys.stderr.write(message.format(self.pidfile)) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, signal.SIGTERM) + time.sleep(0.1) + except OSError as err: + e = str(err.args) + if e.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print (str(err.args)) + sys.exit(1) def restart(self): """Restart the daemon.""" From 82162317077214400286e486cc171ccc6d37a314 Mon Sep 17 00:00:00 2001 From: uenz Date: Sun, 19 Feb 2023 02:35:20 +0100 Subject: [PATCH 28/42] Dont use daemon mode for service --- code/python/upspico/picofssd/systemd/picofssd.service | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/systemd/picofssd.service b/code/python/upspico/picofssd/systemd/picofssd.service index adef686..980e054 100755 --- a/code/python/upspico/picofssd/systemd/picofssd.service +++ b/code/python/upspico/picofssd/systemd/picofssd.service @@ -7,7 +7,9 @@ Restart=always RestartSec=1s StandardOutput=null StandardError=null -ExecStart=/usr/bin/python3 /usr/local/bin/picofssd --log-level info --pid-file /var/run/picofssd.pid --xml-config /etc/pimodules/picofssd/picofssd.xml +#Deamon mode seems not to work +#ExecStart=/usr/bin/python3 /usr/local/bin/picofssd --log-level info --pid-file /var/run/picofssd.pid --xml-config /etc/pimodules/picofssd/picofssd.xml +ExecStart=/usr/bin/python3 /usr/local/bin/picofssd --log-level info -d --xml-config /etc/pimodules/picofssd/picofssd.xml TimeoutSec=30 [Install] From f5868d2150594be2f7d06b9ff85ca4773a4e0713 Mon Sep 17 00:00:00 2001 From: uenz Date: Sun, 15 Oct 2023 20:28:35 +0200 Subject: [PATCH 29/42] Update config_for_pico.sh Changes for bookworm --- code/shell/config_for_pico.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/shell/config_for_pico.sh b/code/shell/config_for_pico.sh index 4a29638..ab079e1 100755 --- a/code/shell/config_for_pico.sh +++ b/code/shell/config_for_pico.sh @@ -10,13 +10,13 @@ set -e echo '--- update' apt-get update echo '--- install some packages' -apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 python3-rpi.gpio # wiringpi +apt-get install -y python3-dev python3-pip python3-serial python3-smbus python3-jinja2 python3-rpi.gpio python3-psutil python3-xmltodict # wiringpi -echo '--- pip install psutil' -pip3 install psutil +# echo '--- pip install psutil' +# pip3 install psutil -echo '--- pip install xmltodict' -pip3 install xmltodict +# echo '--- pip install xmltodict' +# pip3 install xmltodict echo '--- save and edit cmdline.txt' cp /boot/cmdline.txt /boot/cmdline.txt.save From afd1b2edb2213837125570f99c486c52dc183d0d Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Oct 2023 13:54:23 +0200 Subject: [PATCH 30/42] Update setup.py --- code/python/upspico/picofssd/setup.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index c3bfc60..2bb323a 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -16,7 +16,7 @@ """ -from distutils.core import setup +from setuptools import setup doclines = __doc__.split("\n") @@ -25,7 +25,6 @@ datafiles=[ ('/etc/pimodules/picofssd', ['etc/pimodules/picofssd/emailAlertBody.template', 'etc/pimodules/picofssd/emailAlertSubject.template', 'etc/pimodules/picofssd/picofssd.xml']), ('/etc/default', ['default/picofssd']), -# ('/etc/init.d', ['init.d/picofssd']), ('/etc/systemd/system', ['systemd/picofssd.service']) ] @@ -38,7 +37,7 @@ author_email='mike.ray@btinternet.com', url='http://pimodules.com', platforms=['POSIX'], - classifiers = filter(None, classifiers.split("\n")), + classifiers = list(filter(None, classifiers.split("\n"))), scripts=['scripts/picofssd', 'scripts/picofssdxmlconfig'], data_files = datafiles ) From 95029fda037ed18e2ac0c72de91e9326fabae354 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Oct 2023 13:56:01 +0200 Subject: [PATCH 31/42] Update setup.py --- code/python/upspico/picofssd/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index 2bb323a..c708b65 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -40,4 +40,5 @@ classifiers = list(filter(None, classifiers.split("\n"))), scripts=['scripts/picofssd', 'scripts/picofssdxmlconfig'], data_files = datafiles + packages = ['etc','default','systemd'] ) From cf26f484e59e0f5b8fe7907a0405babb83b51f65 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Oct 2023 13:56:44 +0200 Subject: [PATCH 32/42] Update setup.py --- code/python/package/setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/python/package/setup.py b/code/python/package/setup.py index 93daa29..c5fdcb9 100755 --- a/code/python/package/setup.py +++ b/code/python/package/setup.py @@ -15,12 +15,12 @@ """ -from distutils.core import setup +from setuptools import setup doclines = __doc__.split("\n") setup(name='pimodules', - version='0.1dev', + version='0.1.dev0', description=doclines[0], long_description = "\n".join(doclines[2:]), license='GPL3', @@ -28,6 +28,6 @@ author_email='mike.ray@btinternet.com', url='http://pimodules.com', platforms=['POSIX'], - classifiers = filter(None, classifiers.split("\n")), + classifiers = list(filter(None, classifiers.split("\n"))), packages=['pimodules'], ) From 35dc30a00c30f314c6300dba415a896ae1000d10 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Oct 2023 13:57:10 +0200 Subject: [PATCH 33/42] Update setup.py --- code/python/upspico/picofssd/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index c708b65..1132f12 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -29,7 +29,7 @@ ] setup(name='picofssd', - version='0.1dev', + version='0.1.dev0', description=doclines[0], long_description = "\n".join(doclines[2:]), license='GPL3', From ade18de0753f54db1dce7ba4716df5b585db14d4 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Oct 2023 13:59:19 +0200 Subject: [PATCH 34/42] Update setup.py --- code/python/upspico/picofssd/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index 1132f12..fdafdcc 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -39,6 +39,6 @@ platforms=['POSIX'], classifiers = list(filter(None, classifiers.split("\n"))), scripts=['scripts/picofssd', 'scripts/picofssdxmlconfig'], - data_files = datafiles + data_files = datafiles, packages = ['etc','default','systemd'] ) From eff30333e89a673ccbd843c682c363f998db1c61 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 18 Oct 2023 15:17:29 +0200 Subject: [PATCH 35/42] Update setup.py --- code/python/upspico/picofssd/setup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index fdafdcc..e309940 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -24,8 +24,9 @@ datafiles=[ ('/etc/pimodules/picofssd', ['etc/pimodules/picofssd/emailAlertBody.template', 'etc/pimodules/picofssd/emailAlertSubject.template', 'etc/pimodules/picofssd/picofssd.xml']), - ('/etc/default', ['default/picofssd']), - ('/etc/systemd/system', ['systemd/picofssd.service']) + ('/etc/default', ['default/picofssd']), + ('/etc/systemd/system', ['systemd/picofssd.service']), + ('scripts',['scripts/picofssd','scripts/picofssdxmlconfig']) ] setup(name='picofssd', @@ -40,5 +41,5 @@ classifiers = list(filter(None, classifiers.split("\n"))), scripts=['scripts/picofssd', 'scripts/picofssdxmlconfig'], data_files = datafiles, - packages = ['etc','default','systemd'] + packages = ['etc','default','systemd','scripts'] ) From ce97642be8c32eee6bd6ca04706f6ecf605eacc2 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 25 Oct 2023 16:04:32 +0200 Subject: [PATCH 36/42] Made venv possible --- .../upspico/picofssd/scripts/__init__.py | 0 .../scripts/{picofssd => picofssd.py} | 384 +++++++++--------- code/python/upspico/picofssd/setup.cfg | 3 - code/python/upspico/picofssd/setup.py | 85 ++-- .../upspico/picofssd/systemd/picofssd.service | 2 +- 5 files changed, 233 insertions(+), 241 deletions(-) create mode 100755 code/python/upspico/picofssd/scripts/__init__.py rename code/python/upspico/picofssd/scripts/{picofssd => picofssd.py} (84%) diff --git a/code/python/upspico/picofssd/scripts/__init__.py b/code/python/upspico/picofssd/scripts/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/code/python/upspico/picofssd/scripts/picofssd b/code/python/upspico/picofssd/scripts/picofssd.py similarity index 84% rename from code/python/upspico/picofssd/scripts/picofssd rename to code/python/upspico/picofssd/scripts/picofssd.py index 531eb75..860983f 100755 --- a/code/python/upspico/picofssd/scripts/picofssd +++ b/code/python/upspico/picofssd/scripts/picofssd.py @@ -1,192 +1,192 @@ -#!/usr/bin/python3 - -import sys - -if (sys.platform == "linux") or (sys.platform == "linux2"): - pass -else: - # Replace libraries by fake ones - import fake_rpi - - sys.modules['RPi'] = fake_rpi.RPi # Fake RPi - sys.modules['RPi.GPIO'] = fake_rpi.RPi.GPIO # Fake GPIO - sys.modules['smbus'] = fake_rpi.smbus # Fake smbus (I2C) - -from pimodules import configuration -from pimodules.alerts import sendEmail -from pimodules.daemon import Daemon -import RPi.GPIO as GPIO -import socket -import xmltodict -import argparse -import logging.handlers -import logging -import time -import atexit -import signal -import os -""" - -PiModules(R) UPS PIco file-safe shutdown daemon. - -""" - - -CLOCK_PIN = 27 -PULSE_PIN = 22 -BOUNCE_TIME = 30 - - -class fssd(Daemon): - def __init__(self, pidfile, xmlconfig, loglevel=logging.NOTSET): - Daemon.__init__(self, pidfile) - self.loglevel = loglevel - self.log = logging.getLogger(__name__) - self.log.setLevel(self.loglevel) - self.log.setLevel(logging.DEBUG) - if (sys.platform == "linux") or (sys.platform == "linux2"): - handler = logging.handlers.SysLogHandler(address='/dev/log') - else: - handler = logging.StreamHandler(sys.stdout) - formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') - handler.setFormatter(formatter) - self.log.addHandler(handler) - - try: - self.xmlconfig = xmlconfig - with open(self.xmlconfig, 'rb') as fi: - self.config = xmltodict.parse(fi) - except IOError as e: - self.log.warning( - "Failed to load XML config file, loading defaults. Alerts will be disabled") - self.config = xmltodict.parse(configuration.DEFAULT_FSSD_XML_CONFIG) - - self.config = self.config['root']['fssd:config']['fssd:alerts']['fssd:email'] - self.config['fssd:enabled'] = (self.config['fssd:enabled'] == 'True') - - signal.signal(signal.SIGTERM, self.sigcatch) - - self.counter = 0 - self.mail_sent = False - - # first interrupt on isr pin will start pulse high - self.sqwave = True - - def setup(self): - """ - GPIO initialisation - - Note: This cannot go in the __init__ method because the unix double-fork in the generic daemon code - mucks up the initialisation of the GPIO system. - - So it is called in the over-ridden run method. - """ - - GPIO.setmode(GPIO.BCM) - GPIO.setwarnings(False) - GPIO.setup(CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) - GPIO.add_event_detect(CLOCK_PIN, GPIO.FALLING, callback=self.isr, bouncetime=BOUNCE_TIME) - - def isr(self, channel): - """ - GPIO interrupt service routine - """ - # This test is here because the user *might* have another HAT plugged in or another circuit that produces a - # falling-edge signal on another GPIO pin. - if channel != CLOCK_PIN: - return - - # we can get the state of a pin with GPIO.input even when it is currently configured as an output - self.sqwave = not GPIO.input(PULSE_PIN) - - # set pulse pin low before changing it to input to look for shutdown signal - GPIO.output(PULSE_PIN, False) - GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) - if not GPIO.input(PULSE_PIN): - # disable irq to prevent multiple mails - GPIO.remove_event_detect(CLOCK_PIN) - # pin is low, this is shutdown signal from pico - self.counter += 1 - self.log.warning("Lost power supply, Pi will shutdown") - if self.mail_sent == False: - self.mail_sent = True - self.alert_email() - time.sleep(2) - os.system('/sbin/shutdown -h now') - else: - self.counter = 0 - - # change pulse pin back to output with flipped state - GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) - - def sigcatch(self, signum, frame): - """ - Signal handler - """ - - if signum == signal.SIGTERM: - sys.exit(0) - - def cleanup(self): - """ - GPIO cleanup - """ - - self.log.debug("Cleanup") - self.log.info("Stopped") - GPIO.cleanup() - - def alert_email(self): - # emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate - try: - if not self.config['fssd:enabled']: - self.log.debug("Email alert is disabled") - return True - - sendEmail(self.config['fssd:server'], self.config['fssd:username'], self.config['fssd:port'], self.config['fssd:security'], - self.config['fssd:sender-email-address'], self.config['fssd:recipient-email-address'], - self.config['fssd:sender-password'], self.config['fssd:subject-template'], - self.config['fssd:body-template']) - except socket.error as e: - self.log.error(format("Exception in alert_email: %d, %s" % (e.errno, e.strerror))) - except: - self.log.error("Unexpected error in alert_email:", sys.exc_info()[0]) - - def run(self): - """ - Super-class overloaded run method. - """ - self.log.info("Started") - self.log.debug(self.config['fssd:enabled']) - self.log.debug(self.config['fssd:server']) - self.log.debug(self.config['fssd:username']) - self.log.debug(self.config['fssd:sender-email-address']) - self.log.debug(self.config['fssd:recipient-email-address']) - - # register function to cleanup at exit - atexit.register(self.cleanup) - - self.setup() - - while True: - time.sleep(5) - - -# parse the command-line -parser = argparse.ArgumentParser() -parser.add_argument('-l', '--log-level', help="Log level, 'info' or 'debug'", - default='info', choices=['info', 'debug']) -parser.add_argument("-x", "--xml-config", help="XML config file", - default='picofssd.xml', required=True) -group = parser.add_mutually_exclusive_group(required=True) -group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", - action="store_true", default=False) -group.add_argument("-p", "--pid-file", help="PID file") -args = parser.parse_args() - -sd = fssd(args.pid_file, args.xml_config, { - 'info': logging.INFO, 'debug': logging.DEBUG}[args.log_level]) - -# the argument to the start method is opposite of debug -sd.start(not args.debug) +#!/usr/bin/python3 + +import sys + +if (sys.platform == "linux") or (sys.platform == "linux2"): + pass +else: + # Replace libraries by fake ones + import fake_rpi + + sys.modules['RPi'] = fake_rpi.RPi # Fake RPi + sys.modules['RPi.GPIO'] = fake_rpi.RPi.GPIO # Fake GPIO + sys.modules['smbus'] = fake_rpi.smbus # Fake smbus (I2C) + +from pimodules import configuration +from pimodules.alerts import sendEmail +from pimodules.daemon import Daemon +import RPi.GPIO as GPIO +import socket +import xmltodict +import argparse +import logging.handlers +import logging +import time +import atexit +import signal +import os +""" + +PiModules(R) UPS PIco file-safe shutdown daemon. + +""" + + +CLOCK_PIN = 27 +PULSE_PIN = 22 +BOUNCE_TIME = 30 + + +class fssd(Daemon): + def __init__(self, pidfile, xmlconfig, loglevel=logging.NOTSET): + Daemon.__init__(self, pidfile) + self.loglevel = loglevel + self.log = logging.getLogger(__name__) + self.log.setLevel(self.loglevel) + self.log.setLevel(logging.DEBUG) + if (sys.platform == "linux") or (sys.platform == "linux2"): + handler = logging.handlers.SysLogHandler(address='/dev/log') + else: + handler = logging.StreamHandler(sys.stdout) + formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') + handler.setFormatter(formatter) + self.log.addHandler(handler) + + try: + self.xmlconfig = xmlconfig + with open(self.xmlconfig, 'rb') as fi: + self.config = xmltodict.parse(fi) + except IOError as e: + self.log.warning( + "Failed to load XML config file, loading defaults. Alerts will be disabled") + self.config = xmltodict.parse(configuration.DEFAULT_FSSD_XML_CONFIG) + + self.config = self.config['root']['fssd:config']['fssd:alerts']['fssd:email'] + self.config['fssd:enabled'] = (self.config['fssd:enabled'] == 'True') + + signal.signal(signal.SIGTERM, self.sigcatch) + + self.counter = 0 + self.mail_sent = False + + # first interrupt on isr pin will start pulse high + self.sqwave = True + + def setup(self): + """ + GPIO initialisation + + Note: This cannot go in the __init__ method because the unix double-fork in the generic daemon code + mucks up the initialisation of the GPIO system. + + So it is called in the over-ridden run method. + """ + + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + GPIO.setup(CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) + GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) + GPIO.add_event_detect(CLOCK_PIN, GPIO.FALLING, callback=self.isr, bouncetime=BOUNCE_TIME) + + def isr(self, channel): + """ + GPIO interrupt service routine + """ + # This test is here because the user *might* have another HAT plugged in or another circuit that produces a + # falling-edge signal on another GPIO pin. + if channel != CLOCK_PIN: + return + + # we can get the state of a pin with GPIO.input even when it is currently configured as an output + self.sqwave = not GPIO.input(PULSE_PIN) + + # set pulse pin low before changing it to input to look for shutdown signal + GPIO.output(PULSE_PIN, False) + GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) + if not GPIO.input(PULSE_PIN): + # disable irq to prevent multiple mails + GPIO.remove_event_detect(CLOCK_PIN) + # pin is low, this is shutdown signal from pico + self.counter += 1 + self.log.warning("Lost power supply, Pi will shutdown") + if self.mail_sent == False: + self.mail_sent = True + self.alert_email() + time.sleep(2) + os.system('/sbin/shutdown -h now') + else: + self.counter = 0 + + # change pulse pin back to output with flipped state + GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) + + def sigcatch(self, signum, frame): + """ + Signal handler + """ + + if signum == signal.SIGTERM: + sys.exit(0) + + def cleanup(self): + """ + GPIO cleanup + """ + + self.log.debug("Cleanup") + self.log.info("Stopped") + GPIO.cleanup() + + def alert_email(self): + # emailserver, username, port, security, fromAddr, toAddr, b64Password, msgSubjectTemplate, msgBodyTemplate + try: + if not self.config['fssd:enabled']: + self.log.debug("Email alert is disabled") + return True + + sendEmail(self.config['fssd:server'], self.config['fssd:username'], self.config['fssd:port'], self.config['fssd:security'], + self.config['fssd:sender-email-address'], self.config['fssd:recipient-email-address'], + self.config['fssd:sender-password'], self.config['fssd:subject-template'], + self.config['fssd:body-template']) + except socket.error as e: + self.log.error(format("Exception in alert_email: %d, %s" % (e.errno, e.strerror))) + except: + self.log.error("Unexpected error in alert_email:", sys.exc_info()[0]) + + def run(self): + """ + Super-class overloaded run method. + """ + self.log.info("Started") + self.log.debug(self.config['fssd:enabled']) + self.log.debug(self.config['fssd:server']) + self.log.debug(self.config['fssd:username']) + self.log.debug(self.config['fssd:sender-email-address']) + self.log.debug(self.config['fssd:recipient-email-address']) + + # register function to cleanup at exit + atexit.register(self.cleanup) + + self.setup() + + while True: + time.sleep(5) + +if __name__ == "__main__": + # parse the command-line + parser = argparse.ArgumentParser() + parser.add_argument('-l', '--log-level', help="Log level, 'info' or 'debug'", + default='info', choices=['info', 'debug']) + parser.add_argument("-x", "--xml-config", help="XML config file", + default='picofssd.xml', required=True) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", + action="store_true", default=False) + group.add_argument("-p", "--pid-file", help="PID file") + args = parser.parse_args() + + sd = fssd(args.pid_file, args.xml_config, { + 'info': logging.INFO, 'debug': logging.DEBUG}[args.log_level]) + + # the argument to the start method is opposite of debug + sd.start(not args.debug) diff --git a/code/python/upspico/picofssd/setup.cfg b/code/python/upspico/picofssd/setup.cfg index 01a5109..e69de29 100644 --- a/code/python/upspico/picofssd/setup.cfg +++ b/code/python/upspico/picofssd/setup.cfg @@ -1,3 +0,0 @@ -[install] -prefix=/usr/local - diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index e309940..2b6a9c1 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -1,45 +1,40 @@ - -"""picofssd: UPSPIco file-safe shutdown daemon - -This script is intended for use with the PiModules(R) UPSPIco -uninterruptible power supply for use with a Raspberry Pi computer. -""" - -classifiers = """\ -Development Status :: 5 - Testing/Beta -Intended Audience :: PiModules UPS PiCo Product developers and partners (change to customers when published) -License :: GNU General Public License Version 3 -Programming Language :: Python >= 2.7 -Topic :: PiModules(R) -Topic :: UPS PIco support daemon -Operating System :: Linux (Raspbian) -""" - - -from setuptools import setup - -doclines = __doc__.split("\n") - - - -datafiles=[ - ('/etc/pimodules/picofssd', ['etc/pimodules/picofssd/emailAlertBody.template', 'etc/pimodules/picofssd/emailAlertSubject.template', 'etc/pimodules/picofssd/picofssd.xml']), - ('/etc/default', ['default/picofssd']), - ('/etc/systemd/system', ['systemd/picofssd.service']), - ('scripts',['scripts/picofssd','scripts/picofssdxmlconfig']) -] - -setup(name='picofssd', - version='0.1.dev0', - description=doclines[0], - long_description = "\n".join(doclines[2:]), - license='GPL3', - author='Mike Ray', - author_email='mike.ray@btinternet.com', - url='http://pimodules.com', - platforms=['POSIX'], - classifiers = list(filter(None, classifiers.split("\n"))), - scripts=['scripts/picofssd', 'scripts/picofssdxmlconfig'], - data_files = datafiles, - packages = ['etc','default','systemd','scripts'] - ) + +"""picofssd: UPSPIco file-safe shutdown daemon + +This script is intended for use with the PiModules(R) UPSPIco +uninterruptible power supply for use with a Raspberry Pi computer. +""" + +classifiers = """\ +Development Status :: 5 - Testing/Beta +Intended Audience :: PiModules UPS PiCo Product developers and partners (change to customers when published) +License :: GNU General Public License Version 3 +Programming Language :: Python >= 2.7 +Topic :: PiModules(R) +Topic :: UPS PIco support daemon +Operating System :: Linux (Raspbian) +""" + + +from setuptools import setup + +doclines = __doc__.split("\n") + +datafiles=[ + ('.', ['scripts/picofssd.py','scripts/picofssdxmlconfig']) +] + +setup(name='picofssd', + version='0.2.dev0', + description=doclines[0], + long_description = "\n".join(doclines[2:]), + license='GPL3', + author='Mike Ray', + author_email='mike.ray@btinternet.com', + url='http://pimodules.com', + platforms=['POSIX'], + classifiers = list(filter(None, classifiers.split("\n"))), + install_requires=['fake-rpi','xmltodict','jinja2','rpi.gpio'], + packages=['scripts'], + data_files = datafiles + ) diff --git a/code/python/upspico/picofssd/systemd/picofssd.service b/code/python/upspico/picofssd/systemd/picofssd.service index 980e054..e4da48d 100755 --- a/code/python/upspico/picofssd/systemd/picofssd.service +++ b/code/python/upspico/picofssd/systemd/picofssd.service @@ -9,7 +9,7 @@ StandardOutput=null StandardError=null #Deamon mode seems not to work #ExecStart=/usr/bin/python3 /usr/local/bin/picofssd --log-level info --pid-file /var/run/picofssd.pid --xml-config /etc/pimodules/picofssd/picofssd.xml -ExecStart=/usr/bin/python3 /usr/local/bin/picofssd --log-level info -d --xml-config /etc/pimodules/picofssd/picofssd.xml +ExecStart=/usr/bin/python3 -m picofssd --log-level info -d --xml-config /etc/pimodules/picofssd/picofssd.xml TimeoutSec=30 [Install] From 4de48133fa04c912c958fd4b53300eec24515ded Mon Sep 17 00:00:00 2001 From: uenz Date: Tue, 8 Apr 2025 21:11:35 +0200 Subject: [PATCH 37/42] Squashed commit of the following: commit 45ed700e59db66d53c14c0cee70563e3ebb9d8ef Author: uenz Date: Tue Apr 8 20:41:26 2025 +0200 Update pyproject.toml commit 2d74695e485f1c93942604fe7fc7ca5a23247d97 Author: uenz Date: Tue Apr 8 17:33:27 2025 +0200 Update picofssd.py commit 6c59817df1fe4e98553c1089a9e9382bc320f1ce Author: uenz Date: Sun Apr 6 09:49:04 2025 +0200 changed to gpiozero commit 1415587d30493508ec18f234fe4b104ffb7781ff Author: uenz Date: Fri Apr 4 20:30:17 2025 +0200 Changed build system to toml --- code/python/package/pyproject.toml | 26 +++++ code/python/package/setup.py | 44 ++++---- .../picofssd/picofssd.egg-info/requires.txt | 4 + code/python/upspico/picofssd/pyproject.toml | 37 +++++++ .../upspico/picofssd/scripts/picofssd.py | 103 +++++++++--------- code/python/upspico/picofssd/setup.cfg | 0 code/python/upspico/picofssd/setup.py | 60 +++++----- 7 files changed, 171 insertions(+), 103 deletions(-) create mode 100644 code/python/package/pyproject.toml create mode 100644 code/python/upspico/picofssd/picofssd.egg-info/requires.txt create mode 100644 code/python/upspico/picofssd/pyproject.toml delete mode 100644 code/python/upspico/picofssd/setup.cfg diff --git a/code/python/package/pyproject.toml b/code/python/package/pyproject.toml new file mode 100644 index 0000000..96781c1 --- /dev/null +++ b/code/python/package/pyproject.toml @@ -0,0 +1,26 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "pimodules" +version = "0.1.dev0" +description = "UPSPIco file-safe shutdown daemon" +readme = "README.txt" +license = "GPL-3.0-or-later" +authors = [{ name = "Mike Ray", email = "mike.ray@btinternet.com" }] +classifiers = [ + "Development Status :: 5 - Testing/Beta", + "Intended Audience :: PiModules UPS PiCo Product developers and partners (change to customers when published)", + "Programming Language :: Python >= 2.7", + "Topic :: PiModules(R)", + "Topic :: UPS PIco support daemon", + "Operating System :: Linux (Raspbian)", +] +dependencies = ["xmltodict", "jinja2"] + +[project.urls] +homepage = "http://pimodules.com" + +[tool.setuptools] +packages = ["pimodules"] diff --git a/code/python/package/setup.py b/code/python/package/setup.py index c5fdcb9..9f37232 100755 --- a/code/python/package/setup.py +++ b/code/python/package/setup.py @@ -4,30 +4,30 @@ A package which contains code to support PiModules products of various kinds. """ -classifiers = """\ -Development Status :: 5 - Testing/Beta -Intended Audience :: PiModules Product Developers -License :: GNU General Public License Version 3 -Programming Language :: Python >= 2.7 -Topic :: PiModules(R) -Topic :: PiModules(R) Products Python Package -Operating System :: Linux (Raspbian) -""" +# classifiers = """\ +# Development Status :: 5 - Testing/Beta +# Intended Audience :: PiModules Product Developers +# License :: GNU General Public License Version 3 +# Programming Language :: Python >= 2.7 +# Topic :: PiModules(R) +# Topic :: PiModules(R) Products Python Package +# Operating System :: Linux (Raspbian) +# """ from setuptools import setup doclines = __doc__.split("\n") - -setup(name='pimodules', - version='0.1.dev0', - description=doclines[0], - long_description = "\n".join(doclines[2:]), - license='GPL3', - author='Mike Ray', - author_email='mike.ray@btinternet.com', - url='http://pimodules.com', - platforms=['POSIX'], - classifiers = list(filter(None, classifiers.split("\n"))), - packages=['pimodules'], - ) +setup() +# setup(name='pimodules', +# version='0.1.dev0', +# description=doclines[0], +# long_description = "\n".join(doclines[2:]), +# license='GPL3', +# author='Mike Ray', +# author_email='mike.ray@btinternet.com', +# url='http://pimodules.com', +# platforms=['POSIX'], +# classifiers = list(filter(None, classifiers.split("\n"))), +# packages=['pimodules'], +# ) diff --git a/code/python/upspico/picofssd/picofssd.egg-info/requires.txt b/code/python/upspico/picofssd/picofssd.egg-info/requires.txt new file mode 100644 index 0000000..1845c58 --- /dev/null +++ b/code/python/upspico/picofssd/picofssd.egg-info/requires.txt @@ -0,0 +1,4 @@ +fake-rpi +xmltodict +jinja2 +rpi-lgpio diff --git a/code/python/upspico/picofssd/pyproject.toml b/code/python/upspico/picofssd/pyproject.toml new file mode 100644 index 0000000..baeb0dd --- /dev/null +++ b/code/python/upspico/picofssd/pyproject.toml @@ -0,0 +1,37 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +dependencies = [ + 'fake-rpi; os_name != "linux"', + 'xmltodict', + 'jinja2', + 'rpi-lgpio; os_name == "linux"', +] +name = "picofssd" +version = "0.2.dev1" +description = "UPSPIco file-safe shutdown daemon" +readme = "README.txt" +license = "GPL-3.0-or-later" +authors = [{ name = "Mike Ray", email = "mike.ray@btinternet.com" }] +classifiers = [ + "Development Status :: 5 - Testing/Beta", + "Intended Audience :: PiModules UPS PiCo Product developers and partners (change to customers when published)", + "Programming Language :: Python >= 2.7", + "Topic :: PiModules(R)", + "Topic :: UPS PIco support daemon", + "Operating System :: Linux (Raspbian)", +] + +[project.urls] +homepage = "http://pimodules.com" + +[tool.setuptools] +packages = ["scripts"] + +[tool.setuptools.data-files] +"." = ["scripts/picofssd.py", "scripts/picofssdxmlconfig"] + +[project.scripts] +picofssd = "scripts.picofssd:main" diff --git a/code/python/upspico/picofssd/scripts/picofssd.py b/code/python/upspico/picofssd/scripts/picofssd.py index 860983f..abaa9f6 100755 --- a/code/python/upspico/picofssd/scripts/picofssd.py +++ b/code/python/upspico/picofssd/scripts/picofssd.py @@ -1,21 +1,20 @@ -#!/usr/bin/python3 -import sys +#!/usr/bin/python -if (sys.platform == "linux") or (sys.platform == "linux2"): - pass -else: - # Replace libraries by fake ones - import fake_rpi +""" - sys.modules['RPi'] = fake_rpi.RPi # Fake RPi - sys.modules['RPi.GPIO'] = fake_rpi.RPi.GPIO # Fake GPIO - sys.modules['smbus'] = fake_rpi.smbus # Fake smbus (I2C) +PiModules(R) UPS PIco file-safe shutdown daemon. + +""" +import sys # noqa +print(sys.platform) +if sys.platform!="linux": # noqa + print("Faking libraries") + import fake_rpi # noqa + sys.modules['RPi'] = fake_rpi.RPi # noqa Fake RPi + sys.modules['RPi.GPIO'] = fake_rpi.RPi.GPIO # noqa Fake GPIO + sys.modules['smbus'] = fake_rpi.smbus # noqa Fake smbus (I2C) -from pimodules import configuration -from pimodules.alerts import sendEmail -from pimodules.daemon import Daemon -import RPi.GPIO as GPIO import socket import xmltodict import argparse @@ -25,11 +24,10 @@ import atexit import signal import os -""" - -PiModules(R) UPS PIco file-safe shutdown daemon. - -""" +import RPi.GPIO as GPIO +from pimodules.daemon import Daemon +from pimodules.alerts import sendEmail +from pimodules import configuration CLOCK_PIN = 27 @@ -43,23 +41,24 @@ def __init__(self, pidfile, xmlconfig, loglevel=logging.NOTSET): self.loglevel = loglevel self.log = logging.getLogger(__name__) self.log.setLevel(self.loglevel) - self.log.setLevel(logging.DEBUG) - if (sys.platform == "linux") or (sys.platform == "linux2"): + if sys.platform == "Linux": handler = logging.handlers.SysLogHandler(address='/dev/log') else: handler = logging.StreamHandler(sys.stdout) - formatter = logging.Formatter('%(module)s[%(process)s]: <%(levelname)s>: %(message)s') + formatter = logging.Formatter( + '%(module)s[%(process)s]: <%(levelname)s>: %(message)s') handler.setFormatter(formatter) self.log.addHandler(handler) try: self.xmlconfig = xmlconfig - with open(self.xmlconfig, 'rb') as fi: - self.config = xmltodict.parse(fi) + with open(self.xmlconfig, 'rt', encoding='utf-8') as fi: + self.config = xmltodict.parse(fi.read()) except IOError as e: self.log.warning( "Failed to load XML config file, loading defaults. Alerts will be disabled") - self.config = xmltodict.parse(configuration.DEFAULT_FSSD_XML_CONFIG) + self.config = xmltodict.parse( + configuration.DEFAULT_FSSD_XML_CONFIG) self.config = self.config['root']['fssd:config']['fssd:alerts']['fssd:email'] self.config['fssd:enabled'] = (self.config['fssd:enabled'] == 'True') @@ -67,7 +66,6 @@ def __init__(self, pidfile, xmlconfig, loglevel=logging.NOTSET): signal.signal(signal.SIGTERM, self.sigcatch) self.counter = 0 - self.mail_sent = False # first interrupt on isr pin will start pulse high self.sqwave = True @@ -86,7 +84,8 @@ def setup(self): GPIO.setwarnings(False) GPIO.setup(CLOCK_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(PULSE_PIN, GPIO.OUT, initial=self.sqwave) - GPIO.add_event_detect(CLOCK_PIN, GPIO.FALLING, callback=self.isr, bouncetime=BOUNCE_TIME) + GPIO.add_event_detect(CLOCK_PIN, GPIO.FALLING, + callback=self.isr, bouncetime=BOUNCE_TIME) def isr(self, channel): """ @@ -104,14 +103,10 @@ def isr(self, channel): GPIO.output(PULSE_PIN, False) GPIO.setup(PULSE_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) if not GPIO.input(PULSE_PIN): - # disable irq to prevent multiple mails - GPIO.remove_event_detect(CLOCK_PIN) # pin is low, this is shutdown signal from pico self.counter += 1 self.log.warning("Lost power supply, Pi will shutdown") - if self.mail_sent == False: - self.mail_sent = True - self.alert_email() + self.alert_email() time.sleep(2) os.system('/sbin/shutdown -h now') else: @@ -149,14 +144,17 @@ def alert_email(self): self.config['fssd:sender-password'], self.config['fssd:subject-template'], self.config['fssd:body-template']) except socket.error as e: - self.log.error(format("Exception in alert_email: %d, %s" % (e.errno, e.strerror))) + self.log.error( + format("Exception in alert_email: %d, %s" % (e.errno, e.strerror))) except: - self.log.error("Unexpected error in alert_email:", sys.exc_info()[0]) + self.log.error("Unexpected error in alert_email:", + sys.exc_info()[0]) def run(self): """ Super-class overloaded run method. """ + self.log.info("Started") self.log.debug(self.config['fssd:enabled']) self.log.debug(self.config['fssd:server']) @@ -172,21 +170,22 @@ def run(self): while True: time.sleep(5) -if __name__ == "__main__": - # parse the command-line - parser = argparse.ArgumentParser() - parser.add_argument('-l', '--log-level', help="Log level, 'info' or 'debug'", - default='info', choices=['info', 'debug']) - parser.add_argument("-x", "--xml-config", help="XML config file", - default='picofssd.xml', required=True) - group = parser.add_mutually_exclusive_group(required=True) - group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", - action="store_true", default=False) - group.add_argument("-p", "--pid-file", help="PID file") - args = parser.parse_args() - - sd = fssd(args.pid_file, args.xml_config, { - 'info': logging.INFO, 'debug': logging.DEBUG}[args.log_level]) - - # the argument to the start method is opposite of debug - sd.start(not args.debug) + +# parse the command-line +parser = argparse.ArgumentParser() +parser.add_argument('-l', '--log-level', help="Log level, 'info' or 'debug'", + default='info', choices=['info', 'debug']) +parser.add_argument("-x", "--xml-config", help="XML config file", + default='picofssd.xml', required=True) +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("-d", "--debug", help="Keep in the foreground, do not daemonize", + action="store_true", default=False) +group.add_argument("-p", "--pid-file", help="PID file") +args = parser.parse_args() +print(args) +print(args.xml_config) +sd = fssd(args.pid_file, args.xml_config, { + 'info': logging.INFO, 'debug': logging.DEBUG}[args.log_level]) + +# the argument to the start method is opposite of debug +sd.start(not args.debug) diff --git a/code/python/upspico/picofssd/setup.cfg b/code/python/upspico/picofssd/setup.cfg deleted file mode 100644 index e69de29..0000000 diff --git a/code/python/upspico/picofssd/setup.py b/code/python/upspico/picofssd/setup.py index 2b6a9c1..b834f78 100755 --- a/code/python/upspico/picofssd/setup.py +++ b/code/python/upspico/picofssd/setup.py @@ -5,36 +5,38 @@ uninterruptible power supply for use with a Raspberry Pi computer. """ -classifiers = """\ -Development Status :: 5 - Testing/Beta -Intended Audience :: PiModules UPS PiCo Product developers and partners (change to customers when published) -License :: GNU General Public License Version 3 -Programming Language :: Python >= 2.7 -Topic :: PiModules(R) -Topic :: UPS PIco support daemon -Operating System :: Linux (Raspbian) -""" +from setuptools import setup -from setuptools import setup +# classifiers = """\ +# Development Status :: 5 - Testing/Beta +# Intended Audience :: PiModules UPS PiCo Product developers and partners (change to customers when published) +# License :: GNU General Public License Version 3 +# Programming Language :: Python >= 2.7 +# Topic :: PiModules(R) +# Topic :: UPS PIco support daemon +# Operating System :: Linux (Raspbian) +# """ -doclines = __doc__.split("\n") -datafiles=[ - ('.', ['scripts/picofssd.py','scripts/picofssdxmlconfig']) -] - -setup(name='picofssd', - version='0.2.dev0', - description=doclines[0], - long_description = "\n".join(doclines[2:]), - license='GPL3', - author='Mike Ray', - author_email='mike.ray@btinternet.com', - url='http://pimodules.com', - platforms=['POSIX'], - classifiers = list(filter(None, classifiers.split("\n"))), - install_requires=['fake-rpi','xmltodict','jinja2','rpi.gpio'], - packages=['scripts'], - data_files = datafiles - ) +doclines = __doc__.split("\n") +setup() +# moved to toml +# datafiles = [ +# ('.', ['scripts/picofssd.py', 'scripts/picofssdxmlconfig']) +# ] + +# setup(name='picofssd', +# version='0.2.dev1', +# description=doclines[0], +# long_description="\n".join(doclines[2:]), +# license='GPL3', +# author='Mike Ray', +# author_email='mike.ray@btinternet.com', +# url='http://pimodules.com', +# platforms=['POSIX'], +# classifiers=list(filter(None, classifiers.split("\n"))), +# install_requires=['fake-rpi', 'xmltodict', 'jinja2', 'rpi-lgpio'], +# packages=['scripts'], +# data_files=datafiles +# ) From c36ac00de02f4c295931e27317e52921f5c37842 Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 9 Apr 2025 19:34:08 +0200 Subject: [PATCH 38/42] Updated licence in toml --- code/python/package/pyproject.toml | 2 +- code/python/upspico/picofssd/pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/python/package/pyproject.toml b/code/python/package/pyproject.toml index 96781c1..ed360b7 100644 --- a/code/python/package/pyproject.toml +++ b/code/python/package/pyproject.toml @@ -7,7 +7,7 @@ name = "pimodules" version = "0.1.dev0" description = "UPSPIco file-safe shutdown daemon" readme = "README.txt" -license = "GPL-3.0-or-later" +license = {text="GPL-3.0-or-later"} authors = [{ name = "Mike Ray", email = "mike.ray@btinternet.com" }] classifiers = [ "Development Status :: 5 - Testing/Beta", diff --git a/code/python/upspico/picofssd/pyproject.toml b/code/python/upspico/picofssd/pyproject.toml index baeb0dd..aac6600 100644 --- a/code/python/upspico/picofssd/pyproject.toml +++ b/code/python/upspico/picofssd/pyproject.toml @@ -13,7 +13,7 @@ name = "picofssd" version = "0.2.dev1" description = "UPSPIco file-safe shutdown daemon" readme = "README.txt" -license = "GPL-3.0-or-later" +license = {text="GPL-3.0-or-later"} authors = [{ name = "Mike Ray", email = "mike.ray@btinternet.com" }] classifiers = [ "Development Status :: 5 - Testing/Beta", From 3e5b97428b6a7b3a950a26b42b7ed44c3ecf89ff Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 9 Apr 2025 19:53:47 +0200 Subject: [PATCH 39/42] Updated dependencies in toml --- code/python/upspico/picofssd/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/python/upspico/picofssd/pyproject.toml b/code/python/upspico/picofssd/pyproject.toml index aac6600..d027d86 100644 --- a/code/python/upspico/picofssd/pyproject.toml +++ b/code/python/upspico/picofssd/pyproject.toml @@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta" [project] dependencies = [ - 'fake-rpi; os_name != "linux"', + 'fake-rpi; os_name != "Linux"', 'xmltodict', 'jinja2', - 'rpi-lgpio; os_name == "linux"', + 'rpi-lgpio; os_name == "Linux"', ] name = "picofssd" version = "0.2.dev1" From dfd6d57a6f61ae622da00d347416e20cca86a28a Mon Sep 17 00:00:00 2001 From: uenz Date: Wed, 9 Apr 2025 21:00:08 +0200 Subject: [PATCH 40/42] Updated dependencies in toml --- code/python/upspico/picofssd/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/python/upspico/picofssd/pyproject.toml b/code/python/upspico/picofssd/pyproject.toml index d027d86..79208de 100644 --- a/code/python/upspico/picofssd/pyproject.toml +++ b/code/python/upspico/picofssd/pyproject.toml @@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta" [project] dependencies = [ - 'fake-rpi; os_name != "Linux"', + 'fake-rpi; platform_system != "Linux"', 'xmltodict', 'jinja2', - 'rpi-lgpio; os_name == "Linux"', + 'rpi-lgpio; platform_system == "Linux"', ] name = "picofssd" version = "0.2.dev1" From 6b902b7e3e02555b20b436e772403d6993f306a2 Mon Sep 17 00:00:00 2001 From: uenz Date: Mon, 17 Nov 2025 15:49:03 +0100 Subject: [PATCH 41/42] Update pyproject.toml --- code/python/package/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/package/pyproject.toml b/code/python/package/pyproject.toml index ed360b7..96781c1 100644 --- a/code/python/package/pyproject.toml +++ b/code/python/package/pyproject.toml @@ -7,7 +7,7 @@ name = "pimodules" version = "0.1.dev0" description = "UPSPIco file-safe shutdown daemon" readme = "README.txt" -license = {text="GPL-3.0-or-later"} +license = "GPL-3.0-or-later" authors = [{ name = "Mike Ray", email = "mike.ray@btinternet.com" }] classifiers = [ "Development Status :: 5 - Testing/Beta", From 6de7a69e3dbc104f014b4ce3ea92b4f7e5456404 Mon Sep 17 00:00:00 2001 From: uenz Date: Mon, 17 Nov 2025 15:53:02 +0100 Subject: [PATCH 42/42] Update pyproject.toml --- code/python/upspico/picofssd/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/python/upspico/picofssd/pyproject.toml b/code/python/upspico/picofssd/pyproject.toml index 79208de..417211a 100644 --- a/code/python/upspico/picofssd/pyproject.toml +++ b/code/python/upspico/picofssd/pyproject.toml @@ -13,7 +13,7 @@ name = "picofssd" version = "0.2.dev1" description = "UPSPIco file-safe shutdown daemon" readme = "README.txt" -license = {text="GPL-3.0-or-later"} +license = "GPL-3.0-or-later" authors = [{ name = "Mike Ray", email = "mike.ray@btinternet.com" }] classifiers = [ "Development Status :: 5 - Testing/Beta",