From cb464b9738cec2872b1bc6e51d1152671cf250f2 Mon Sep 17 00:00:00 2001 From: Robert Habermann Date: Sun, 7 Jan 2024 12:25:14 +0100 Subject: [PATCH 1/3] extensive reformatting Apply extensive reformatting in regard to code style and syntax. Mostly PEP8 changes (e.g. snake case for functions and variable names. Also clean up some dead code. Switch from _thread to public API. --- ISP-RPi-mqtt-daemon.py | 1418 ++++++++++++++++++++-------------------- 1 file changed, 717 insertions(+), 701 deletions(-) diff --git a/ISP-RPi-mqtt-daemon.py b/ISP-RPi-mqtt-daemon.py index 68f6d83..1b70157 100755 --- a/ISP-RPi-mqtt-daemon.py +++ b/ISP-RPi-mqtt-daemon.py @@ -1,31 +1,25 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import _thread -from datetime import datetime, timedelta -from tzlocal import get_localzone -import threading -import socket +import argparse +import json import os -import subprocess -import uuid +import os.path import ssl +import subprocess import sys -import re -import json -import os.path -import argparse -from time import time, sleep, localtime, strftime +import threading from collections import OrderedDict -from colorama import init as colorama_init -from colorama import Fore, Back, Style from configparser import ConfigParser -from unidecode import unidecode -import paho.mqtt.client as mqtt -import sdnotify +from datetime import datetime from signal import signal, SIGPIPE, SIG_DFL -signal(SIGPIPE, SIG_DFL) -import time +from time import time, sleep, localtime, strftime + +import paho.mqtt.client as mqtt import requests +import sdnotify +from colorama import Fore, Style +from tzlocal import get_localzone +from unidecode import unidecode from urllib3.exceptions import InsecureRequestWarning apt_available = True @@ -34,41 +28,55 @@ except ImportError: apt_available = False +# make sure this script is not run on Python 2.x +if sys.version_info[0] < 3: + sys.stderr.write('Sorry, this script requires a python3 runtime environment.') + sys.exit(1) + script_version = "1.8.5" script_name = 'ISP-RPi-mqtt-daemon.py' script_info = '{} v{}'.format(script_name, script_version) project_name = 'RPi Reporter MQTT2HA Daemon' project_url = 'https://github.com/ironsheep/RPi-Reporter-MQTT2HA-Daemon' -# we'll use this throughout -local_tz = get_localzone() +signal(SIGPIPE, SIG_DFL) # turn off insecure connection warnings (our KZ0Q site has bad certs) # REF: https://www.geeksforgeeks.org/how-to-disable-security-certificate-checks-for-requests-in-python/ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) -# TODO: -# - add announcement of free-space and temperatore endpoints - -if False: - # will be caught by python 2.7 to be illegal syntax - print_line( - 'Sorry, this script requires a python3 runtime environment.', file=sys.stderr) - os._exit(1) - -# Argparse -opt_debug = False -opt_verbose = False +# we'll use this throughout +local_tz = get_localzone() # Systemd Service Notifications - https://github.com/bb4242/sdnotify sd_notifier = sdnotify.SystemdNotifier() -# Logging function +# TODO: +# - add announcement of free-space and temperature endpoints + +# Argparse +parser = argparse.ArgumentParser( + description=project_name, epilog='For further details see: ' + project_url) +parser.add_argument("-v", "--verbose", + help="increase output verbosity", action="store_true") +parser.add_argument( + "-d", "--debug", help="show debug output", action="store_true") +parser.add_argument( + "-s", "--stall", help="TEST: report only the first time", action="store_true") +parser.add_argument("-c", '--config_dir', + help='set directory where config.ini is located', default=sys.path[0]) +parse_args = parser.parse_args() + +config_dir = parse_args.config_dir +opt_debug = parse_args.debug +opt_verbose = parse_args.verbose +opt_stall = parse_args.stall def print_line(text, error=False, warning=False, info=False, verbose=False, debug=False, console=True, sd_notify=False): + """Logging function""" timestamp = strftime('%Y-%m-%d %H:%M:%S', localtime()) - if(sd_notify): + if sd_notify: text = '* NOTIFY: {}'.format(text) if console: if error: @@ -94,35 +102,17 @@ def print_line(text, error=False, warning=False, info=False, verbose=False, debu sd_notifier.notify( 'STATUS={} - {}.'.format(timestamp_sd, unidecode(text))) -# Identifier cleanup - +# Identifier cleanup def clean_identifier(name): clean = name.strip() - for this, that in [[' ', '-'], ['ä', 'ae'], ['Ä', 'Ae'], ['ö', 'oe'], ['Ö', 'Oe'], ['ü', 'ue'], ['Ü', 'Ue'], ['ß', 'ss']]: + for this, that in [[' ', '-'], ['ä', 'ae'], ['Ä', 'Ae'], ['ö', 'oe'], ['Ö', 'Oe'], ['ü', 'ue'], ['Ü', 'Ue'], + ['ß', 'ss']]: clean = clean.replace(this, that) clean = unidecode(clean) return clean -# Argparse -parser = argparse.ArgumentParser( - description=project_name, epilog='For further details see: ' + project_url) -parser.add_argument("-v", "--verbose", - help="increase output verbosity", action="store_true") -parser.add_argument( - "-d", "--debug", help="show debug output", action="store_true") -parser.add_argument( - "-s", "--stall", help="TEST: report only the first time", action="store_true") -parser.add_argument("-c", '--config_dir', - help='set directory where config.ini is located', default=sys.path[0]) -parse_args = parser.parse_args() - -config_dir = parse_args.config_dir -opt_debug = parse_args.debug -opt_verbose = parse_args.verbose -opt_stall = parse_args.stall - print_line('--------------------------------------------------------------------', debug=True) print_line(script_info, info=True) if opt_verbose: @@ -139,103 +129,110 @@ def clean_identifier(name): # Eclipse Paho callbacks - http://www.eclipse.org/paho/clients/python/docs/#callbacks mqtt_client_connected = False -print_line( - '* init mqtt_client_connected=[{}]'.format(mqtt_client_connected), debug=True) +print_line('* init mqtt_client_connected=[{}]'.format(mqtt_client_connected), debug=True) mqtt_client_should_attempt_reconnect = True + def on_connect(client, userdata, flags, rc): global mqtt_client_connected if rc == 0: print_line('* MQTT connection established', console=True, sd_notify=True) print_line('') # blank line?! - #_thread.start_new_thread(afterMQTTConnect, ()) + + # threading.Thread(target=afterMQTTConnect).start() + mqtt_client_connected = True - print_line('on_connect() mqtt_client_connected=[{}]'.format( - mqtt_client_connected), debug=True) + print_line('on_connect() mqtt_client_connected=[{}]'.format(mqtt_client_connected), debug=True) # ------------------------------------------------------------------------- # Commands Subscription - if (len(commands) > 0): + # ------------------------------------------------------------------------- + if len(commands) > 0: print_line('MQTT subscription to {}/+ enabled'.format(command_base_topic), console=True, sd_notify=True) mqtt_client.subscribe('{}/+'.format(command_base_topic)) else: - print_line('MQTT subscripton to {}/+ disabled'.format(command_base_topic), console=True, sd_notify=True) - # ------------------------------------------------------------------------- + print_line('MQTT subscription to {}/+ disabled'.format(command_base_topic), console=True, sd_notify=True) else: print_line('! Connection error with result code {} - {}'.format(str(rc), - mqtt.connack_string(rc)), error=True) + mqtt.connack_string(rc)), error=True) print_line('MQTT Connection error with result code {} - {}'.format(str(rc), - mqtt.connack_string(rc)), error=True, sd_notify=True) + mqtt.connack_string(rc)), error=True, + sd_notify=True) # technically NOT useful but readying possible new shape... mqtt_client_connected = False - print_line('on_connect() mqtt_client_connected=[{}]'.format( - mqtt_client_connected), debug=True, error=True) + print_line('on_connect() mqtt_client_connected=[{}]'.format(mqtt_client_connected), debug=True, error=True) # kill main thread - os._exit(1) + sys.exit(1) + def on_disconnect(client, userdata, mid): global mqtt_client_connected mqtt_client_connected = False print_line('* MQTT connection lost', console=True, sd_notify=True) - print_line('on_disconnect() mqtt_client_connected=[{}]'.format( - mqtt_client_connected), debug=True) - pass + print_line('on_disconnect() mqtt_client_connected=[{}]'.format(mqtt_client_connected), debug=True) + def on_publish(client, userdata, mid): - #print_line('* Data successfully published.') - pass + # ToDo(frennkie): Consider setting this back to "pass" as it was + print_line('* Data successfully published.', debug=True) + # ----------------------------------------------------------------------------- # Commands - MQTT Subscription Callback # ----------------------------------------------------------------------------- # Command catalog def on_subscribe(client, userdata, mid, granted_qos): - print_line('on_subscribe() - {} - {}'.format(str(mid),str(granted_qos)), debug=True, sd_notify=True) + print_line('on_subscribe() - {} - {}'.format(str(mid), str(granted_qos)), debug=True, sd_notify=True) + + +shell_cmd_fspec = None + -shell_cmd_fspec = '' def on_message(client, userdata, message): global shell_cmd_fspec - if shell_cmd_fspec == '': - shell_cmd_fspec = getShellCmd() + if not shell_cmd_fspec: + shell_cmd_fspec = get_shell_cmd() if shell_cmd_fspec == '': print_line('* Failed to locate shell Command!', error=True) # kill main thread - os._exit(1) + sys.exit(1) decoded_payload = message.payload.decode('utf-8') - command = message.topic.split('/')[-1] - print_line('on_message() Topic=[{}] payload=[{}] command=[{}]'.format(message.topic, message.payload, command), console=True, sd_notify=True, debug=True) - - if command != 'status': - if command in commands: - print_line('- Command "{}" Received - Run {} {} -'.format(command, commands[command], decoded_payload), console=True, debug=True) - pHandle = subprocess.Popen([shell_cmd_fspec, "-c", commands[command].format(decoded_payload)]) + _command = message.topic.split('/')[-1] + print_line('on_message() Topic=[{}] payload=[{}] _command=[{}]'.format(message.topic, message.payload, _command), + console=True, sd_notify=True, debug=True) + + if _command != 'status': + if _command in commands: + print_line('- Command "{}" Received - Run {} {} -'.format(_command, commands[_command], decoded_payload), + console=True, debug=True) + pHandle = subprocess.Popen([shell_cmd_fspec, "-c", commands[_command].format(decoded_payload)]) output, errors = pHandle.communicate() if errors: print_line('- Command exec says: errors=[{}]'.format(errors), console=True, debug=True) else: print_line('* Invalid Command received.', error=True) + # ----------------------------------------------------------------------------- # Load configuration file -config = ConfigParser(delimiters=( - '=', ), inline_comment_prefixes=('#'), interpolation=None) +# ----------------------------------------------------------------------------- +config = ConfigParser(delimiters=('=',), inline_comment_prefixes='#', interpolation=None) config.optionxform = str try: with open(os.path.join(config_dir, 'config.ini')) as config_file: config.read_file(config_file) except IOError: - print_line('No configuration file "config.ini"', - error=True, sd_notify=True) + print_line('No configuration file "config.ini"', error=True, sd_notify=True) sys.exit(1) daemon_enabled = config['Daemon'].getboolean('enabled', True) +# ToDo(frennkie): This (update_flag_filespec) is not used anywhere # This script uses a flag file containing a date/timestamp of when the system was last updated default_update_flag_filespec = '/home/pi/bin/lastupd.date' -update_flag_filespec = config['Daemon'].get( - 'update_flag_filespec', default_update_flag_filespec) +update_flag_filespec = config['Daemon'].get('update_flag_filespec', default_update_flag_filespec) default_base_topic = 'home/nodes' base_topic = config['MQTT'].get('base_topic', default_base_topic).lower() @@ -243,35 +240,31 @@ def on_message(client, userdata, message): default_sensor_name = 'rpi-reporter' sensor_name = config['MQTT'].get('sensor_name', default_sensor_name).lower() -# by default Home Assistant listens to the /homeassistant but it can be changed for a given installation +# by default Home Assistant listens to the /homeassistant, but it can be changed for a given installation default_discovery_prefix = 'homeassistant' -discovery_prefix = config['MQTT'].get( - 'discovery_prefix', default_discovery_prefix).lower() +discovery_prefix = config['MQTT'].get('discovery_prefix', default_discovery_prefix).lower() # report our RPi values every 5min min_interval_in_minutes = 1 max_interval_in_minutes = 30 default_interval_in_minutes = 5 -interval_in_minutes = config['Daemon'].getint( - 'interval_in_minutes', default_interval_in_minutes) +interval_in_minutes = config['Daemon'].getint('interval_in_minutes', default_interval_in_minutes) # check our RPi pending-updates every 4 hours min_check_interval_in_hours = 2 max_check_interval_in_hours = 24 default_check_interval_in_hours = 4 -check_interval_in_hours = config['Daemon'].getint( - 'check_updates_in_hours', default_check_interval_in_hours) +check_interval_in_hours = config['Daemon'].getint('check_updates_in_hours', default_check_interval_in_hours) # default domain when hostname -f doesn't return it default_domain = '' -fallback_domain = config['Daemon'].get( - 'fallback_domain', default_domain).lower() +fallback_domain = config['Daemon'].get('fallback_domain', default_domain).lower() commands = OrderedDict([]) if config.has_section('Commands'): - commandSet = dict(config['Commands'].items()) - if len(commandSet) > 0: - commands.update(commandSet) + command_set = dict(config['Commands'].items()) + if len(command_set) > 0: + commands.update(command_set) # ----------------------------------------------------------------------------- # Commands Subscription @@ -280,13 +273,17 @@ def on_message(client, userdata, message): # Check configuration # if (interval_in_minutes < min_interval_in_minutes) or (interval_in_minutes > max_interval_in_minutes): - print_line('ERROR: Invalid "interval_in_minutes" found in configuration file: "config.ini"! Must be [{}-{}] Fix and try again... Aborting'.format( - min_interval_in_minutes, max_interval_in_minutes), error=True, sd_notify=True) + print_line('ERROR: Invalid "interval_in_minutes" found in configuration ' + 'file: "config.ini"! Must be [{}-{}] Fix and try again... ' + 'Aborting'.format(min_interval_in_minutes, max_interval_in_minutes), + error=True, sd_notify=True) sys.exit(1) if (check_interval_in_hours < min_check_interval_in_hours) or (check_interval_in_hours > max_check_interval_in_hours): - print_line('ERROR: Invalid "check_updates_in_hours" found in configuration file: "config.ini"! Must be [{}-{}] Fix and try again... Aborting'.format( - min_check_interval_in_hours, max_check_interval_in_hours), error=True, sd_notify=True) + print_line('ERROR: Invalid "check_updates_in_hours" found in configuration ' + 'file: "config.ini"! Must be [{}-{}] Fix and try again... ' + 'Aborting'.format(min_check_interval_in_hours, max_check_interval_in_hours), + error=True, sd_notify=True) sys.exit(1) # Ensure required values within sections of our config are present @@ -301,52 +298,52 @@ def on_message(client, userdata, message): # Daemon variables monitored # ----------------------------------------------------------------------------- -daemon_version_list = [ 'NOT-LOADED' ] +daemon_version_list = ['NOT-LOADED'] daemon_last_fetch_time = 0.0 -def getDaemonReleases(): -# retrieve latest formal release versions list from repo + +def get_daemon_releases(): + # retrieve latest formal release versions list from repo global daemon_version_list global daemon_last_fetch_time - newVersionList = [] + new_version_list = [] latestVersion = '' response = requests.request('GET', 'http://kz0q.com/daemon-releases', verify=False) if response.status_code != 200: - print_line('- getDaemonReleases() RQST status=({})'.format(response.status_code), error=True) - daemon_version_list = [ 'NOT-LOADED' ] # mark as NOT fetched + print_line('- get_daemon_releases() RQST status=({})'.format(response.status_code), error=True) + daemon_version_list = ['NOT-LOADED'] # mark as NOT fetched else: content = response.text lines = content.split('\n') for line in lines: if len(line) > 0: - #print_line('- RLS Line=[{}]'.format(line), debug=True) - lineParts = line.split(' ') - #print_line('- RLS lineParts=[{}]'.format(lineParts), debug=True) - if len(lineParts) >= 2: - currVersion = lineParts[0] - rlsType = lineParts[1] - if not currVersion in newVersionList: - if not 'latest' in rlsType.lower(): - newVersionList.append(currVersion) # append to list + # print_line('- RLS Line=[{}]'.format(line), debug=True) + line_parts = line.split(' ') + # print_line('- RLS line_parts=[{}]'.format(line_parts), debug=True) + if len(line_parts) >= 2: + curr_version = line_parts[0] + rls_type = line_parts[1] + if curr_version not in new_version_list: + if 'latest' not in rls_type.lower(): + new_version_list.append(curr_version) # append to list else: - latestVersion = currVersion + latestVersion = curr_version - if len(newVersionList) > 1: - newVersionList.sort() + if len(new_version_list) > 1: + new_version_list.sort() if len(latestVersion) > 0: - if not latestVersion in newVersionList: - newVersionList.insert(0, latestVersion) # append to list + if latestVersion not in new_version_list: + new_version_list.insert(0, latestVersion) # append to list - daemon_version_list = newVersionList + daemon_version_list = new_version_list print_line('- RQST daemon_version_list=({})'.format(daemon_version_list), debug=True) - daemon_last_fetch_time = time.time() # record when we last fetched the versions - -getDaemonReleases() # and load them! -print_line('* daemon_last_fetch_time=({})'.format(daemon_last_fetch_time), debug=True) + daemon_last_fetch_time = time() # record when we last fetched the versions +get_daemon_releases() # and load them! +print_line('* daemon_last_fetch_time=({})'.format(daemon_last_fetch_time), debug=True) # ----------------------------------------------------------------------------- # RPi variables monitored @@ -364,7 +361,7 @@ def getDaemonReleases(): rpi_uptime = '' rpi_uptime_sec = 0 rpi_last_update_date = datetime.min -#rpi_last_update_date_v2 = datetime.min +# rpi_last_update_date_v2 = datetime.min rpi_filesystem_space_raw = '' rpi_filesystem_space = '' rpi_filesystem_percent = '' @@ -375,9 +372,9 @@ def getDaemonReleases(): rpi_interfaces = [] rpi_filesystem = [] # Tuple (Total, Free, Avail., Swap Total, Swap Free) -rpi_memory_tuple = '' +rpi_memory_tuple = () # Tuple (Hardware, Model Name, NbrCores, BogoMIPS, Serial) -rpi_cpu_tuple = '' +rpi_cpu_tuple = () # for thermal status reporting rpi_throttle_status = [] # new cpu loads @@ -386,18 +383,18 @@ def getDaemonReleases(): rpi_cpuload15 = '' rpi_update_count = 0 -if apt_available == False: - rpi_update_count = -1 # if packaging system not avail. report -1 +if not apt_available: + rpi_update_count = -1 # if packaging system not avail. report -1 # Time for network transfer calculation -previous_time = time.time() +previous_time = time() + # ----------------------------------------------------------------------------- # monitor variable fetch routines -# - +# ----------------------------------------------------------------------------- -def getDeviceCpuInfo(): +def get_device_cpu_info(): global rpi_cpu_tuple # cat /proc/cpuinfo | /bin/egrep -i "processor|model|bogo|hardware|serial" # MULTI-CORE @@ -423,35 +420,36 @@ def getDeviceCpuInfo(): # Hardware : BCM2835 # Serial : 00000000131030c0 # Model : Raspberry Pi Zero W Rev 1.1 - out = subprocess.Popen("cat /proc/cpuinfo | /bin/egrep -i 'processor|model|bogo|hardware|serial'", + cmd_string = '/bin/cat /proc/cpuinfo | /bin/egrep -i "processor|model|bogo|hardware|serial"' + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - trimmedLines = [] - for currLine in lines: - trimmedLine = currLine.lstrip().rstrip() - trimmedLines.append(trimmedLine) - cpu_hardware = '' # 'hardware' - cpu_cores = 0 # count of 'processor' lines - cpu_model = '' # 'model name' + trimmed_lines = [] + for curr_line in lines: + trimmed_line = curr_line.lstrip().rstrip() + trimmed_lines.append(trimmed_line) + cpu_hardware = '' # 'hardware' + cpu_cores = 0 # count of 'processor' lines + _cpu_model = '' # 'model name' cpu_bogoMIPS = 0.0 # sum of 'BogoMIPS' lines - cpu_serial = '' # 'serial' - for currLine in trimmedLines: - lineParts = currLine.split(':') + cpu_serial = '' # 'serial' + for curr_line in trimmed_lines: + lineParts = curr_line.split(':') currValue = '{?unk?}' if len(lineParts) >= 2: currValue = lineParts[1].lstrip().rstrip() - if 'Hardware' in currLine: + if 'Hardware' in curr_line: cpu_hardware = currValue - if 'model name' in currLine: - cpu_model = currValue - if 'BogoMIPS' in currLine: + if 'model name' in curr_line: + _cpu_model = currValue + if 'BogoMIPS' in curr_line: cpu_bogoMIPS += float(currValue) - if 'processor' in currLine: + if 'processor' in curr_line: cpu_cores += 1 - if 'Serial' in currLine: + if 'Serial' in curr_line: cpu_serial = currValue out = subprocess.Popen("/bin/cat /proc/loadavg", @@ -466,12 +464,12 @@ def getDeviceCpuInfo(): cpu_load15 = round(float(float(cpu_loads_raw[2]) / int(cpu_cores) * 100), 1) # Tuple (Hardware, Model Name, NbrCores, BogoMIPS, Serial) - rpi_cpu_tuple = (cpu_hardware, cpu_model, cpu_cores, + rpi_cpu_tuple = (cpu_hardware, _cpu_model, cpu_cores, cpu_bogoMIPS, cpu_serial, cpu_load1, cpu_load5, cpu_load15) print_line('rpi_cpu_tuple=[{}]'.format(rpi_cpu_tuple), debug=True) -def getDeviceMemory(): +def get_device_memory(): global rpi_memory_tuple # $ cat /proc/meminfo | /bin/egrep -i "mem[TFA]" # MemTotal: 948304 kB @@ -483,37 +481,40 @@ def getDeviceMemory(): stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - trimmedLines = [] - for currLine in lines: - trimmedLine = currLine.lstrip().rstrip() - trimmedLines.append(trimmedLine) + trimmed_lines = [] + for curr_line in lines: + trimmed_line = curr_line.lstrip().rstrip() + trimmed_lines.append(trimmed_line) mem_total = '' mem_free = '' mem_avail = '' swap_total = '' swap_free = '' - for currLine in trimmedLines: - lineParts = currLine.split() - if 'MemTotal' in currLine: - mem_total = float(lineParts[1]) / 1024 - if 'MemFree' in currLine: - mem_free = float(lineParts[1]) / 1024 - if 'MemAvail' in currLine: - mem_avail = float(lineParts[1]) / 1024 - if 'SwapTotal' in currLine: - swap_total = float(lineParts[1]) / 1024 - if 'SwapFree' in currLine: - swap_free = float(lineParts[1]) / 1024 + for curr_line in trimmed_lines: + line_parts = curr_line.split() + if 'MemTotal' in curr_line: + mem_total = float(line_parts[1]) / 1024 + if 'MemFree' in curr_line: + mem_free = float(line_parts[1]) / 1024 + if 'MemAvail' in curr_line: + mem_avail = float(line_parts[1]) / 1024 + if 'SwapTotal' in curr_line: + swap_total = float(line_parts[1]) / 1024 + if 'SwapFree' in curr_line: + swap_free = float(line_parts[1]) / 1024 # Tuple (Total, Free, Avail., Swap Total, Swap Free) - rpi_memory_tuple = (mem_total, mem_free, mem_avail, swap_total, swap_free) # [0]=total, [1]=free, [2]=avail., [3]=swap total, [4]=swap free + rpi_memory_tuple = (mem_total, mem_free, mem_avail, swap_total, + swap_free) # [0]=total, [1]=free, [2]=avail., [3]=swap total, [4]=swap free print_line('rpi_memory_tuple=[{}]'.format(rpi_memory_tuple), debug=True) -def getDeviceModel(): + +def get_device_model(): global rpi_model global rpi_model_raw global rpi_connections - out = subprocess.Popen("/bin/cat /proc/device-tree/model | /bin/sed -e 's/\\x0//g'", + cmd_string = '/bin/cat /proc/device-tree/model | /bin/sed -e "s/\\x0//g"' + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -543,9 +544,11 @@ def getDeviceModel(): print_line('rpi_connections=[{}]'.format(rpi_connections), debug=True) -def getLinuxRelease(): +def get_linux_release(): global rpi_linux_release - out = subprocess.Popen("/bin/cat /etc/apt/sources.list | /bin/egrep -v '#' | /usr/bin/awk '{ print $3 }' | /bin/sed -e 's/-/ /g' | /usr/bin/cut -f1 -d' ' | /bin/grep . | /usr/bin/sort -u", + cmd_string = ('/bin/cat /etc/apt/sources.list | /bin/egrep -v "#" | /usr/bin/awk \'{ print $3 }\' | ' + '/bin/sed -e "s/-/ /g" | /usr/bin/cut -f1 -d" " | /bin/grep . | /usr/bin/sort -u') + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -554,7 +557,7 @@ def getLinuxRelease(): print_line('rpi_linux_release=[{}]'.format(rpi_linux_release), debug=True) -def getLinuxVersion(): +def get_linux_version(): global rpi_linux_version out = subprocess.Popen("/bin/uname -r", shell=True, @@ -565,7 +568,7 @@ def getLinuxVersion(): print_line('rpi_linux_version=[{}]'.format(rpi_linux_version), debug=True) -def getHostnames(): +def get_hostnames(): global rpi_hostname global rpi_fqdn out = subprocess.Popen("/bin/hostname -f", @@ -592,7 +595,7 @@ def getHostnames(): print_line('rpi_hostname=[{}]'.format(rpi_hostname), debug=True) -def getUptime(): +def get_uptime(): global rpi_uptime_raw global rpi_uptime global rpi_uptime_sec @@ -606,7 +609,7 @@ def getUptime(): basicParts = rpi_uptime_raw.split() timeStamp = basicParts[0] lineParts = rpi_uptime_raw.split(',') - if('user' in lineParts[1]): + if 'user' in lineParts[1]: rpi_uptime_raw = lineParts[0] else: rpi_uptime_raw = '{}, {}'.format(lineParts[0], lineParts[1]) @@ -616,43 +619,44 @@ def getUptime(): # Ex: 10 days, 23:57 # Ex: 27 days, 27 min # Ex: 0 min - bHasColon = (':' in rpi_uptime) - uptimeParts = rpi_uptime.split(',') - print_line('- uptimeParts=[{}]'.format(uptimeParts), debug=True) - if len(uptimeParts) > 1: + + # b_has_colon = (':' in rpi_uptime) # is not used + uptime_parts = rpi_uptime.split(',') + print_line('- uptime_parts=[{}]'.format(uptime_parts), debug=True) + if len(uptime_parts) > 1: # have days and time - dayParts = uptimeParts[0].strip().split(' ') - daysVal = int(dayParts[0]) - timeStr = uptimeParts[1].strip() + day_parts = uptime_parts[0].strip().split(' ') + days_val = int(day_parts[0]) + time_str = uptime_parts[1].strip() else: # have time only - daysVal = 0 - timeStr = uptimeParts[0].strip() - print_line('- days=({}), timeStr=[{}]'.format(daysVal, timeStr), debug=True) - if ':' in timeStr: - # timeStr = '23:57' - timeParts = timeStr.split(':') - hoursVal = int(timeParts[0]) - minsVal = int(timeParts[1]) + days_val = 0 + time_str = uptime_parts[0].strip() + print_line('- days=({}), time_str=[{}]'.format(days_val, time_str), debug=True) + if ':' in time_str: + # time_str = '23:57' + time_parts = time_str.split(':') + hours_val = int(time_parts[0]) + mins_val = int(time_parts[1]) else: - # timeStr = 27 of: '27 min' - hoursVal = 0 - timeParts = timeStr.split(' ') - minsVal = int(timeParts[0]) - print_line('- hoursVal=({}), minsVal=({})'.format(hoursVal, minsVal), debug=True) - rpi_uptime_sec = (minsVal * 60) + (hoursVal * 60 * 60) + (daysVal * 24 * 60 * 60) + # time_str = 27 of: '27 min' + hours_val = 0 + time_parts = time_str.split(' ') + mins_val = int(time_parts[0]) + print_line('- hours_val=({}), minsVal=({})'.format(hours_val, mins_val), debug=True) + rpi_uptime_sec = (mins_val * 60) + (hours_val * 60 * 60) + (days_val * 24 * 60 * 60) print_line('rpi_uptime_sec=({})'.format(rpi_uptime_sec), debug=True) -def getNetworkIFsUsingIP(ip_cmd): - cmd_str = '{} link show | /bin/egrep -v "link" | /bin/egrep " eth| wlan"'.format( - ip_cmd) + +def get_network_ifs_using_ip(ip_cmd): + cmd_str = '{} link show | /bin/egrep -v "link" | /bin/egrep " eth| wlan"'.format(ip_cmd) out = subprocess.Popen(cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - interfaceNames = [] + interface_names = [] line_count = len(lines) if line_count > 2: line_count = 2 @@ -660,45 +664,45 @@ def getNetworkIFsUsingIP(ip_cmd): print_line('ERROR no lines left by ip(8) filter!', error=True) sys.exit(1) - for lineIdx in range(line_count): - trimmedLine = lines[lineIdx].lstrip().rstrip() - if len(trimmedLine) > 0: - lineParts = trimmedLine.split() - interfaceName = lineParts[1].replace(':', '') + for line_idx in range(line_count): + trimmed_line = lines[line_idx].lstrip().rstrip() + if len(trimmed_line) > 0: + line_parts = trimmed_line.split() + interface_name = line_parts[1].replace(':', '') # if interface is within a container then we have eth0@if77 - interfaceNames.append(interfaceName) + interface_names.append(interface_name) - print_line('interfaceNames=[{}]'.format(interfaceNames), debug=True) + print_line('interface_names=[{}]'.format(interface_names), debug=True) - trimmedLines = [] - for interface in interfaceNames: - lines = getSingleInterfaceDetails(interface) - for currLine in lines: - trimmedLines.append(currLine) + trimmed_lines = [] + for interface in interface_names: + lines = get_single_interface_details(interface) + for curr_line in lines: + trimmed_lines.append(curr_line) - loadNetworkIFDetailsFromLines(trimmedLines) + load_network_if_details_from_lines(trimmed_lines) -def getSingleInterfaceDetails(interfaceName): - cmdString = '/sbin/ifconfig {} | /bin/egrep "Link|flags|inet |ether |TX packets |RX packets "'.format( - interfaceName) - out = subprocess.Popen(cmdString, +def get_single_interface_details(interface_name): + cmd_string = '/sbin/ifconfig {} | /bin/egrep "Link|flags|inet |ether |TX packets |RX packets "'.format( + interface_name) + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - trimmedLines = [] - for currLine in lines: - trimmedLine = currLine.lstrip().rstrip() - if len(trimmedLine) > 0: - trimmedLines.append(trimmedLine) + trimmed_lines = [] + for curr_line in lines: + trimmed_line = curr_line.lstrip().rstrip() + if len(trimmed_line) > 0: + trimmed_lines.append(trimmed_line) - #print_line('interface:[{}] trimmedLines=[{}]'.format(interfaceName, trimmedLines), debug=True) - return trimmedLines + # print_line('interface:[{}] trimmed_lines=[{}]'.format(interfaceName, trimmed_lines), debug=True) + return trimmed_lines -def loadNetworkIFDetailsFromLines(ifConfigLines): +def load_network_if_details_from_lines(if_config_lines): global rpi_interfaces global rpi_mac global previous_time @@ -719,73 +723,74 @@ def loadNetworkIFDetailsFromLines(ifConfigLines): # RX packets 1358790 bytes 1197368205 (1.1 GiB) # TX packets 916361 bytes 150440804 (143.4 MiB) # - tmpInterfaces = [] - haveIF = False + tmp_interfaces = [] + have_if = False imterfc = '' rpi_mac = '' - current_time = time.time() + current_time = time() if current_time == previous_time: current_time += 1 - for currLine in ifConfigLines: - lineParts = currLine.split() - #print_line('- currLine=[{}]'.format(currLine), debug=True) - #print_line('- lineParts=[{}]'.format(lineParts), debug=True) - if len(lineParts) > 0: + for curr_line in if_config_lines: + line_parts = curr_line.split() + # print_line('- curr_line=[{}]'.format(curr_line), debug=True) + # print_line('- line_parts=[{}]'.format(line_parts), debug=True) + if len(line_parts) > 0: # skip interfaces generated by Home Assistant on RPi - if 'docker' in currLine or 'veth' in currLine or 'hassio' in currLine: - haveIF = False + if 'docker' in curr_line or 'veth' in curr_line or 'hassio' in curr_line: + have_if = False continue # let's evaluate remaining interfaces - if 'flags' in currLine: # NEWER ONLY - haveIF = True - imterfc = lineParts[0].replace(':', '') - #print_line('newIF=[{}]'.format(imterfc), debug=True) - elif 'Link' in currLine: # OLDER ONLY - haveIF = True - imterfc = lineParts[0].replace(':', '') - newTuple = (imterfc, 'mac', lineParts[4]) + if 'flags' in curr_line: # NEWER ONLY + have_if = True + imterfc = line_parts[0].replace(':', '') + # print_line('newIF=[{}]'.format(imterfc), debug=True) + elif 'Link' in curr_line: # OLDER ONLY + have_if = True + imterfc = line_parts[0].replace(':', '') + new_tuple = (imterfc, 'mac', line_parts[4]) if rpi_mac == '': - rpi_mac = lineParts[4] + rpi_mac = line_parts[4] print_line('rpi_mac=[{}]'.format(rpi_mac), debug=True) - tmpInterfaces.append(newTuple) - print_line('newTuple=[{}]'.format(newTuple), debug=True) - elif haveIF == True: - print_line('IF=[{}], lineParts=[{}]'.format( - imterfc, lineParts), debug=True) - if 'inet' in currLine: # OLDER & NEWER - newTuple = (imterfc, 'IP', - lineParts[1].replace('addr:', '')) - tmpInterfaces.append(newTuple) - print_line('newTuple=[{}]'.format(newTuple), debug=True) - elif 'ether' in currLine: # NEWER ONLY - newTuple = (imterfc, 'mac', lineParts[1]) - tmpInterfaces.append(newTuple) + tmp_interfaces.append(new_tuple) + print_line('new_tuple=[{}]'.format(new_tuple), debug=True) + elif have_if: + print_line('IF=[{}], line_parts=[{}]'.format( + imterfc, line_parts), debug=True) + if 'inet' in curr_line: # OLDER & NEWER + new_tuple = (imterfc, 'IP', + line_parts[1].replace('addr:', '')) + tmp_interfaces.append(new_tuple) + print_line('new_tuple=[{}]'.format(new_tuple), debug=True) + elif 'ether' in curr_line: # NEWER ONLY + new_tuple = (imterfc, 'mac', line_parts[1]) + tmp_interfaces.append(new_tuple) if rpi_mac == '': - rpi_mac = lineParts[1] + rpi_mac = line_parts[1] print_line('rpi_mac=[{}]'.format(rpi_mac), debug=True) - print_line('newTuple=[{}]'.format(newTuple), debug=True) - elif 'RX' in currLine: # NEWER ONLY - previous_value = getPreviousNetworkData(imterfc, 'rx_data') - current_value = int(lineParts[4]) + print_line('new_tuple=[{}]'.format(new_tuple), debug=True) + elif 'RX' in curr_line: # NEWER ONLY + previous_value = get_previous_network_data(imterfc, 'rx_data') + current_value = int(line_parts[4]) rx_data = round((current_value - previous_value) / (current_time - previous_time) * 8 / 1024) - newTuple = (imterfc, 'rx_data', rx_data) - tmpInterfaces.append(newTuple) - print_line('newTuple=[{}]'.format(newTuple), debug=True) - elif 'TX' in currLine: # NEWER ONLY - previous_value = getPreviousNetworkData(imterfc, 'tx_data') - current_value = int(lineParts[4]) + new_tuple = (imterfc, 'rx_data', rx_data) + tmp_interfaces.append(new_tuple) + print_line('new_tuple=[{}]'.format(new_tuple), debug=True) + elif 'TX' in curr_line: # NEWER ONLY + previous_value = get_previous_network_data(imterfc, 'tx_data') + current_value = int(line_parts[4]) tx_data = round((current_value - previous_value) / (current_time - previous_time) * 8 / 1024) - newTuple = (imterfc, 'tx_data', tx_data) - tmpInterfaces.append(newTuple) - print_line('newTuple=[{}]'.format(newTuple), debug=True) - haveIF = False + new_tuple = (imterfc, 'tx_data', tx_data) + tmp_interfaces.append(new_tuple) + print_line('new_tuple=[{}]'.format(new_tuple), debug=True) + have_if = False - rpi_interfaces = tmpInterfaces + rpi_interfaces = tmp_interfaces print_line('rpi_interfaces=[{}]'.format(rpi_interfaces), debug=True) print_line('rpi_mac=[{}]'.format(rpi_mac), debug=True) -def getPreviousNetworkData(interface, field): + +def get_previous_network_data(interface, field): global rpi_interfaces value = [item for item in rpi_interfaces if item[0] == interface and item[1] == field] if len(value) > 0: @@ -793,29 +798,32 @@ def getPreviousNetworkData(interface, field): else: return 0 -def getNetworkIFs(): - ip_cmd = getIPCmd() - if ip_cmd != '': - getNetworkIFsUsingIP(ip_cmd) + +def get_network_ifs(): + ip_cmd = get_ip_cmd() + if ip_cmd: + get_network_ifs_using_ip(ip_cmd) else: - out = subprocess.Popen('/sbin/ifconfig | /bin/egrep "Link|flags|inet |ether " | /bin/egrep -v -i "lo:|loopback|inet6|\:\:1|127\.0\.0\.1"', + cmd_string = ('/sbin/ifconfig | /bin/egrep "Link|flags|inet |ether " | ' + '/bin/egrep -v -i "lo:|loopback|inet6|\:\:1|127\.0\.0\.1"') + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - trimmedLines = [] - for currLine in lines: - trimmedLine = currLine.lstrip().rstrip() - if len(trimmedLine) > 0: - trimmedLines.append(trimmedLine) + trimmed_lines = [] + for curr_line in lines: + trimmed_line = curr_line.lstrip().rstrip() + if len(trimmed_line) > 0: + trimmed_lines.append(trimmed_line) - print_line('trimmedLines=[{}]'.format(trimmedLines), debug=True) + print_line('trimmed_lines=[{}]'.format(trimmed_lines), debug=True) - loadNetworkIFDetailsFromLines(trimmedLines) + load_network_if_details_from_lines(trimmed_lines) -def getFileSystemDrives(): +def get_file_system_drives(): global rpi_filesystem_space_raw global rpi_filesystem_space global rpi_filesystem_percent @@ -826,14 +834,13 @@ def getFileSystemDrives(): stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - trimmedLines = [] - for currLine in lines: - trimmedLine = currLine.lstrip().rstrip() - if len(trimmedLine) > 0: - trimmedLines.append(trimmedLine) + trimmed_lines = [] + for curr_line in lines: + trimmed_line = curr_line.lstrip().rstrip() + if len(trimmed_line) > 0: + trimmed_lines.append(trimmed_line) - print_line('getFileSystemDrives() trimmedLines=[{}]'.format( - trimmedLines), debug=True) + print_line('get_file_system_drives() trimmed_lines=[{}]'.format(trimmed_lines), debug=True) # EXAMPLES # @@ -865,17 +872,15 @@ def getFileSystemDrives(): # '/dev/sda1 953868 882178 71690 93% /media/usb0', # '/dev/sdb1 976761 93684 883078 10% /media/pi/SSD']] - tmpDrives = [] - for currLine in trimmedLines: - if 'no such device' in currLine.lower(): - print_line('BAD LINE FORMAT, Skipped=[{}]'.format(currLine), debug=True, warning=True) + tmp_drives = [] + for curr_line in trimmed_lines: + if 'no such device' in curr_line.lower(): + print_line('BAD LINE FORMAT, Skipped=[{}]'.format(curr_line), debug=True, warning=True) continue - lineParts = currLine.split() - print_line('lineParts({})=[{}]'.format( - len(lineParts), lineParts), debug=True) - if len(lineParts) < 6: - print_line('BAD LINE FORMAT, Skipped=[{}]'.format( - lineParts), debug=True, warning=True) + line_parts = curr_line.split() + print_line('line_parts({})=[{}]'.format(len(line_parts), line_parts), debug=True) + if len(line_parts) < 6: + print_line('BAD LINE FORMAT, Skipped=[{}]'.format(line_parts), debug=True, warning=True) continue # tuple { total blocks, used%, mountPoint, device } # @@ -887,44 +892,42 @@ def getFileSystemDrives(): # # locate our % used field... - for percent_field_index in range(len(lineParts) - 2, 1, -1): - if '%' in lineParts[percent_field_index]: + percent_field_index = 0 + for percent_field_index in range(len(line_parts) - 2, 1, -1): + if '%' in line_parts[percent_field_index]: break - print_line('percent_field_index=[{}]'.format( - percent_field_index), debug=True) + print_line('percent_field_index=[{}]'.format(percent_field_index), debug=True) total_size_idx = percent_field_index - 3 mount_idx = percent_field_index + 1 # do we have a two part device name? - device = lineParts[0] + device = line_parts[0] if total_size_idx != 1: - device = '{} {}'.format(lineParts[0], lineParts[1]) + device = '{} {}'.format(line_parts[0], line_parts[1]) print_line('device=[{}]'.format(device), debug=True) # do we have a two part mount point? - mount_point = lineParts[mount_idx] - if len(lineParts) - 1 > mount_idx: + mount_point = line_parts[mount_idx] + if len(line_parts) - 1 > mount_idx: mount_point = '{} {}'.format( - lineParts[mount_idx], lineParts[mount_idx + 1]) + line_parts[mount_idx], line_parts[mount_idx + 1]) print_line('mount_point=[{}]'.format(mount_point), debug=True) total_size_in_gb = '{:.0f}'.format( - next_power_of_2(lineParts[total_size_idx])) - newTuple = (total_size_in_gb, lineParts[percent_field_index].replace( - '%', ''), mount_point, device) - tmpDrives.append(newTuple) - print_line('newTuple=[{}]'.format(newTuple), debug=True) - if newTuple[2] == '/': - rpi_filesystem_space_raw = currLine - rpi_filesystem_space = newTuple[0] - rpi_filesystem_percent = newTuple[1] - print_line('rpi_filesystem_space=[{}GB]'.format( - newTuple[0]), debug=True) - print_line('rpi_filesystem_percent=[{}]'.format( - newTuple[1]), debug=True) - - rpi_filesystem = tmpDrives + next_power_of_2(line_parts[total_size_idx])) + new_tuple = (total_size_in_gb, line_parts[percent_field_index].replace( + '%', ''), mount_point, device) + tmp_drives.append(new_tuple) + print_line('new_tuple=[{}]'.format(new_tuple), debug=True) + if new_tuple[2] == '/': + rpi_filesystem_space_raw = curr_line + rpi_filesystem_space = new_tuple[0] + rpi_filesystem_percent = new_tuple[1] + print_line('rpi_filesystem_space=[{}GB]'.format(new_tuple[0]), debug=True) + print_line('rpi_filesystem_percent=[{}]'.format(new_tuple[1]), debug=True) + + rpi_filesystem = tmp_drives print_line('rpi_filesystem=[{}]'.format(rpi_filesystem), debug=True) @@ -933,61 +936,71 @@ def next_power_of_2(size): return 1 if size == 0 else (1 << size_as_nbr.bit_length()) / 1024 -def getVcGenCmd(): +def get_vc_gen_cmd(): cmd_locn1 = '/usr/bin/vcgencmd' cmd_locn2 = '/opt/vc/bin/vcgencmd' - desiredCommand = cmd_locn1 - if os.path.exists(desiredCommand) == False: - desiredCommand = cmd_locn2 - if os.path.exists(desiredCommand) == False: - desiredCommand = '' - if desiredCommand != '': - print_line('Found vcgencmd(1)=[{}]'.format(desiredCommand), debug=True) - return desiredCommand - -def getShellCmd(): + desired_command = None + if os.path.exists(cmd_locn1): + desired_command = cmd_locn1 + elif os.path.exists(cmd_locn2): + desired_command = cmd_locn2 + else: + print_line('ERROR: vcgencmd(8) not found!', error=True) + + if desired_command: + print_line('Found vcgencmd(8)=[{}]'.format(desired_command), debug=True) + else: + pass # ToDo: maybe exit? + return desired_command + + +def get_shell_cmd(): cmd_locn1 = '/usr/bin/sh' cmd_locn2 = '/bin/sh' - desiredCommand = cmd_locn1 - if os.path.exists(desiredCommand) == False: - desiredCommand = cmd_locn2 - if os.path.exists(desiredCommand) == False: - desiredCommand = '' - if desiredCommand != '': - print_line('Found sh(1)=[{}]'.format(desiredCommand), debug=True) - return desiredCommand - -def getIPCmd(): + desired_command = None + if os.path.exists(cmd_locn1): + desired_command = cmd_locn1 + elif os.path.exists(cmd_locn2): + desired_command = '' + else: + print_line('ERROR: sh(1) not found!', error=True) + + if desired_command: + print_line('Found sh(1)=[{}]'.format(desired_command), debug=True) + else: + pass # ToDo: maybe exit? + return desired_command + + +def get_ip_cmd(): cmd_locn1 = '/bin/ip' cmd_locn2 = '/sbin/ip' - desiredCommand = '' - if os.path.exists(cmd_locn1) == True: - desiredCommand = cmd_locn1 - elif os.path.exists(cmd_locn2) == True: - desiredCommand = cmd_locn2 - if desiredCommand != '': - print_line('Found IP(8)=[{}]'.format(desiredCommand), debug=True) - return desiredCommand + desired_command = None + if os.path.exists(cmd_locn1): + desired_command = cmd_locn1 + elif os.path.exists(cmd_locn2): + desired_command = cmd_locn2 + else: + print_line('ERROR: ip(8) not found!', error=True) + + if desired_command: + print_line('Found ip(8)=[{}]'.format(desired_command), debug=True) + else: + pass # ToDo: maybe exit? + return desired_command -def getSystemTemperature(): +def get_system_temperature(): global rpi_system_temp global rpi_gpu_temp global rpi_cpu_temp rpi_gpu_temp_raw = 'failed' - cmd_fspec = getVcGenCmd() - if cmd_fspec == '': - rpi_system_temp = float('-1.0') - rpi_gpu_temp = float('-1.0') - rpi_cpu_temp = getSystemCPUTemperature() - if rpi_cpu_temp != -1.0: - rpi_system_temp = rpi_cpu_temp - else: + + cmd_fspec = get_vc_gen_cmd() + if cmd_fspec and os.access("/dev/vcio", os.R_OK): retry_count = 3 while retry_count > 0 and 'failed' in rpi_gpu_temp_raw: - - cmd_string = "{} measure_temp | /bin/sed -e 's/\\x0//g'".format( - cmd_fspec) + cmd_string = "{} measure_temp | /bin/sed -e 's/\\x0//g'".format(cmd_fspec) out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, @@ -999,39 +1012,47 @@ def getSystemTemperature(): sleep(1) if 'failed' in rpi_gpu_temp_raw: - interpretedTemp = float('-1.0') + interpreted_temp = float('-1.0') else: - interpretedTemp = float(rpi_gpu_temp_raw) - rpi_gpu_temp = interpretedTemp + interpreted_temp = float(rpi_gpu_temp_raw) + rpi_gpu_temp = interpreted_temp print_line('rpi_gpu_temp=[{}]'.format(rpi_gpu_temp), debug=True) - rpi_cpu_temp = getSystemCPUTemperature() + rpi_cpu_temp = get_system_cpu_temperature() - # fallback to CPU temp is GPU not available + # fallback to CPU temp if is GPU not available rpi_system_temp = rpi_gpu_temp if rpi_gpu_temp == -1.0: rpi_system_temp = rpi_cpu_temp + else: + # fallback to CPU temp if is GPU not available + print_line('- (WARN): GPU temp not available - falling back to CPU'.format(rpi_gpu_temp), warning=True) + rpi_system_temp = float('-1.0') + rpi_gpu_temp = float('-1.0') + rpi_cpu_temp = get_system_cpu_temperature() + if rpi_cpu_temp != -1.0: + rpi_system_temp = rpi_cpu_temp + -def getSystemCPUTemperature(): +def get_system_cpu_temperature(): cmd_locn1 = '/sys/class/thermal/thermal_zone0/temp' - cmdString = '/bin/cat {}'.format( - cmd_locn1) - if os.path.exists(cmd_locn1) == False: - rpi_cpu_temp = float('-1.0') + cmd_string = '/bin/cat {}'.format(cmd_locn1) + if not os.path.exists(cmd_locn1): + _rpi_cpu_temp = float('-1.0') else: - out = subprocess.Popen(cmdString, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + out = subprocess.Popen(cmd_string, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) stdout, _ = out.communicate() rpi_cpu_temp_raw = stdout.decode('utf-8').rstrip() - rpi_cpu_temp = float(rpi_cpu_temp_raw) / 1000.0 - print_line('rpi_cpu_temp=[{}]'.format(rpi_cpu_temp), debug=True) - return rpi_cpu_temp + _rpi_cpu_temp = float(rpi_cpu_temp_raw) / 1000.0 + print_line('_rpi_cpu_temp=[{}]'.format(_rpi_cpu_temp), debug=True) + return _rpi_cpu_temp -def getSystemThermalStatus(): +def get_system_thermal_status(): global rpi_throttle_status # sudo vcgencmd get_throttled # throttled=0x0 @@ -1039,7 +1060,7 @@ def getSystemThermalStatus(): # REF: https://harlemsquirrel.github.io/shell/2019/01/05/monitoring-raspberry-pi-power-and-thermal-issues.html # rpi_throttle_status = [] - cmd_fspec = getVcGenCmd() + cmd_fspec = get_vc_gen_cmd() if cmd_fspec == '': rpi_throttle_status.append('Not Available') else: @@ -1050,20 +1071,17 @@ def getSystemThermalStatus(): stderr=subprocess.STDOUT) stdout, _ = out.communicate() rpi_throttle_status_raw = stdout.decode('utf-8').rstrip() - print_line('rpi_throttle_status_raw=[{}]'.format( - rpi_throttle_status_raw), debug=True) + print_line('rpi_throttle_status_raw=[{}]'.format(rpi_throttle_status_raw), debug=True) - if not 'throttled' in rpi_throttle_status_raw: - rpi_throttle_status.append( - 'bad response [{}] from vcgencmd'.format(rpi_throttle_status_raw)) + if 'throttled' not in rpi_throttle_status_raw: + rpi_throttle_status.append('bad response [{}] from vcgencmd'.format(rpi_throttle_status_raw)) else: values = [] - lineParts = rpi_throttle_status_raw.split('=') - print_line('lineParts=[{}]'.format(lineParts), debug=True) + line_parts = rpi_throttle_status_raw.split('=') + print_line('line_parts=[{}]'.format(line_parts), debug=True) rpi_throttle_value_raw = '' - if len(lineParts) > 1: - rpi_throttle_value_raw = lineParts[1] - rpi_throttle_value = int(0) + if len(line_parts) > 1: + rpi_throttle_value_raw = line_parts[1] if len(rpi_throttle_value_raw) > 0: values.append('throttled = {}'.format(rpi_throttle_value_raw)) if rpi_throttle_value_raw.startswith('0x'): @@ -1071,18 +1089,18 @@ def getSystemThermalStatus(): else: rpi_throttle_value = int(rpi_throttle_value_raw, 10) # decode test code - #rpi_throttle_value = int('0x50002', 16) + # rpi_throttle_value = int('0x50002', 16) if rpi_throttle_value > 0: - values = interpretThrottleValue(rpi_throttle_value) + values = interpret_throttle_value(rpi_throttle_value) else: values.append('Not throttled') if len(values) > 0: rpi_throttle_status = values - print_line('rpi_throttle_status=[{}]'.format( - rpi_throttle_status), debug=True) + print_line('rpi_throttle_status=[{}]'.format(rpi_throttle_status), debug=True) + -def interpretThrottleValue(throttleValue): +def interpret_throttle_value(throttle_value): """ 01110000000000000010 |||| ||||_ Under-voltage detected @@ -1094,82 +1112,78 @@ def interpretThrottleValue(throttleValue): ||_ Throttling has occurred |_ Soft temperature limit has occurred """ - print_line('throttleValue=[{}]'.format(bin(throttleValue)), debug=True) - interpResult = [] + print_line('throttleValue=[{}]'.format(bin(throttle_value)), debug=True) + interp_result = [] meanings = [ - (2**0, 'Under-voltage detected'), - (2**1, 'Arm frequency capped'), - (2**2, 'Currently throttled'), - (2**3, 'Soft temperature limit active'), - (2**16, 'Under-voltage has occurred'), - (2**17, 'Arm frequency capped has occurred'), - (2**18, 'Throttling has occurred'), - (2**19, 'Soft temperature limit has occurred'), + (2 ** 0, 'Under-voltage detected'), + (2 ** 1, 'Arm frequency capped'), + (2 ** 2, 'Currently throttled'), + (2 ** 3, 'Soft temperature limit active'), + (2 ** 16, 'Under-voltage has occurred'), + (2 ** 17, 'Arm frequency capped has occurred'), + (2 ** 18, 'Throttling has occurred'), + (2 ** 19, 'Soft temperature limit has occurred'), ] - for meaningIndex in range(len(meanings)): - bitTuple = meanings[meaningIndex] - if throttleValue & bitTuple[0] > 0: - interpResult.append(bitTuple[1]) + for meaning_index in range(len(meanings)): + bit_tuple = meanings[meaning_index] + if throttle_value & bit_tuple[0] > 0: + interp_result.append(bit_tuple[1]) - print_line('interpResult=[{}]'.format(interpResult), debug=True) - return interpResult + print_line('interp_result=[{}]'.format(interp_result), debug=True) + return interp_result -def getLastUpdateDate(): +def get_last_update_date(): global rpi_last_update_date # apt-get update writes to following dir (so date changes on update) apt_listdir_filespec = '/var/lib/apt/lists/partial' # apt-get dist-upgrade | autoremove update the following file when actions are taken apt_lockdir_filespec = '/var/lib/dpkg/lock' - cmdString = '/bin/ls -ltrd {} {}'.format( - apt_listdir_filespec, apt_lockdir_filespec) - out = subprocess.Popen(cmdString, + cmd_string = '/bin/ls -ltrd {} {}'.format(apt_listdir_filespec, apt_lockdir_filespec) + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = out.communicate() lines = stdout.decode('utf-8').split("\n") - trimmedLines = [] - for currLine in lines: - trimmedLine = currLine.lstrip().rstrip() - if len(trimmedLine) > 0: - trimmedLines.append(trimmedLine) - print_line('trimmedLines=[{}]'.format(trimmedLines), debug=True) - - fileSpec_latest = '' - if len(trimmedLines) > 0: - lastLineIdx = len(trimmedLines) - 1 - lineParts = trimmedLines[lastLineIdx].split() - if len(lineParts) > 0: - lastPartIdx = len(lineParts) - 1 - fileSpec_latest = lineParts[lastPartIdx] - print_line('fileSpec_latest=[{}]'.format(fileSpec_latest), debug=True) - - fileModDateInSeconds = os.path.getmtime(fileSpec_latest) - fileModDate = datetime.fromtimestamp(fileModDateInSeconds) - rpi_last_update_date = fileModDate.replace(tzinfo=local_tz) + trimmed_lines = [] + for curr_line in lines: + trimmed_line = curr_line.lstrip().rstrip() + if len(trimmed_line) > 0: + trimmed_lines.append(trimmed_line) + print_line('trimmed_lines=[{}]'.format(trimmed_lines), debug=True) + + file_spec_latest = '' + if len(trimmed_lines) > 0: + last_line_idx = len(trimmed_lines) - 1 + line_parts = trimmed_lines[last_line_idx].split() + if len(line_parts) > 0: + lastPartIdx = len(line_parts) - 1 + file_spec_latest = line_parts[lastPartIdx] + print_line('file_spec_latest=[{}]'.format(file_spec_latest), debug=True) + + file_mod_date_in_seconds = os.path.getmtime(file_spec_latest) + file_mod_date = datetime.fromtimestamp(file_mod_date_in_seconds) + rpi_last_update_date = file_mod_date.replace(tzinfo=local_tz) print_line('rpi_last_update_date=[{}]'.format( rpi_last_update_date), debug=True) -def to_datetime(time): - return datetime.fromordinal(int(time)) + datetime.timedelta(time % 1) - - -def getLastInstallDate(): +def get_last_install_date(): global rpi_last_update_date - #apt_log_filespec = '/var/log/dpkg.log' - #apt_log_filespec2 = '/var/log/dpkg.log.1' - out = subprocess.Popen("/bin/grep --binary-files=text 'status installed' /var/log/dpkg.log /var/log/dpkg.log.1 2>/dev/null | sort | tail -1", + # apt_log_filespec = '/var/log/dpkg.log' + # apt_log_filespec2 = '/var/log/dpkg.log.1' + cmd_string = ("/bin/grep --binary-files=text 'status installed' /var/log/dpkg.log " + "/var/log/dpkg.log.1 2>/dev/null | sort | tail -1") + out = subprocess.Popen(cmd_string, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = out.communicate() last_installed_pkg_raw = stdout.decode( 'utf-8').rstrip().replace('/var/log/dpkg.log:', '').replace('/var/log/dpkg.log.1:', '') - print_line('last_installed_pkg_raw=[{}]'.format( - last_installed_pkg_raw), debug=True) + print_line('last_installed_pkg_raw=[{}]'.format(last_installed_pkg_raw), debug=True) line_parts = last_installed_pkg_raw.split() if len(line_parts) > 1: pkg_date_string = '{} {}'.format(line_parts[0], line_parts[1]) @@ -1181,12 +1195,13 @@ def getLastInstallDate(): pkg_date_string, '%Y-%m-%d %H:%M:%S').replace(tzinfo=local_tz) rpi_last_update_date = pkg_install_date - print_line('rpi_last_update_date=[{}]'.format( - rpi_last_update_date), debug=True) + print_line('rpi_last_update_date=[{}]'.format(rpi_last_update_date), debug=True) + update_last_fetch_time = 0.0 -def getNumberOfAvailableUpdates(): + +def get_number_of_available_updates(): global rpi_update_count global update_last_fetch_time if apt_available: @@ -1198,20 +1213,21 @@ def getNumberOfAvailableUpdates(): print_line('APT Avail Updates: ({})'.format(len(changes)), info=True) # return str(cache.get_changes().len()) rpi_update_count = len(changes) - update_last_fetch_time = time.time() + update_last_fetch_time = time() + -# get our hostnames so we can setup MQTT -getHostnames() -if(sensor_name == default_sensor_name): +# get our hostnames so we can set up MQTT +get_hostnames() +if sensor_name == default_sensor_name: sensor_name = 'rpi-{}'.format(rpi_hostname) # get model so we can use it too in MQTT -getDeviceModel() -getDeviceCpuInfo() -getLinuxRelease() -getLinuxVersion() -getFileSystemDrives() +get_device_model() +get_device_cpu_info() +get_linux_release() +get_linux_version() +get_file_system_drives() if apt_available: - getNumberOfAvailableUpdates() + get_number_of_available_updates() # ----------------------------------------------------------------------------- # MQTT Topic def's @@ -1226,51 +1242,51 @@ def getNumberOfAvailableUpdates(): K_ALIVE_TIMOUT_IN_SECONDS = 60 -def publishAliveStatus(): +def publish_alive_status(): print_line('- SEND: yes, still alive -', debug=True) mqtt_client.publish(lwt_sensor_topic, payload=lwt_online_val, retain=False) mqtt_client.publish(lwt_command_topic, payload=lwt_online_val, retain=False) -def publishShuttingDownStatus(): + +def publish_shutting_down_status(): print_line('- SEND: shutting down -', debug=True) mqtt_client.publish(lwt_sensor_topic, payload=lwt_offline_val, retain=False) mqtt_client.publish(lwt_command_topic, payload=lwt_offline_val, retain=False) -def aliveTimeoutHandler(): + +def alive_timeout_handler(): print_line('- MQTT TIMER INTERRUPT -', debug=True) - _thread.start_new_thread(publishAliveStatus, ()) - startAliveTimer() - - -def startAliveTimer(): - global aliveTimer - global aliveTimerRunningStatus - stopAliveTimer() - aliveTimer = threading.Timer(K_ALIVE_TIMOUT_IN_SECONDS, aliveTimeoutHandler) - aliveTimer.start() - aliveTimerRunningStatus = True - print_line( - '- started MQTT timer - every {} seconds'.format(K_ALIVE_TIMOUT_IN_SECONDS), debug=True) - - -def stopAliveTimer(): - global aliveTimer - global aliveTimerRunningStatus - aliveTimer.cancel() - aliveTimerRunningStatus = False + threading.Thread(target=publish_alive_status).start() + start_alive_timer() + + +def start_alive_timer(): + global alive_timer + global alive_timer_running_status + stop_alive_timer() + alive_timer = threading.Timer(K_ALIVE_TIMOUT_IN_SECONDS, alive_timeout_handler) + alive_timer.start() + alive_timer_running_status = True + print_line('- started MQTT timer - every {} seconds'.format(K_ALIVE_TIMOUT_IN_SECONDS), debug=True) + + +def stop_alive_timer(): + global alive_timer + global alive_timer_running_status + alive_timer.cancel() + alive_timer_running_status = False print_line('- stopped MQTT timer', debug=True) -def isAliveTimerRunning(): - global aliveTimerRunningStatus - return aliveTimerRunningStatus +def is_alive_timer_running(): + global alive_timer_running_status + return alive_timer_running_status # our ALIVE TIMER -aliveTimer = threading.Timer(K_ALIVE_TIMOUT_IN_SECONDS, aliveTimeoutHandler) +alive_timer = threading.Timer(K_ALIVE_TIMOUT_IN_SECONDS, alive_timeout_handler) # our BOOL tracking state of ALIVE TIMER -aliveTimerRunningStatus = False - +alive_timer_running_status = False # ----------------------------------------------------------------------------- # MQTT setup and startup @@ -1296,7 +1312,7 @@ def isAliveTimerRunning(): if config['MQTT'].getboolean('tls', False): # According to the docs, setting PROTOCOL_SSLv23 "Selects the highest protocol version # that both the client and server support. Despite the name, this option can select - # “TLS” protocols as well as “SSL”" - so this seems like a resonable default + # “TLS” protocols as well as “SSL”" - so this seems like a reasonable default mqtt_client.tls_set( ca_certs=config['MQTT'].get('tls_ca_cert', None), keyfile=config['MQTT'].get('tls_keyfile', None), @@ -1324,12 +1340,12 @@ def isAliveTimerRunning(): mqtt_client.publish(lwt_command_topic, payload=lwt_online_val, retain=False) mqtt_client.loop_start() - while mqtt_client_connected == False: # wait in loop + while not mqtt_client_connected: # wait in loop print_line( '* Wait on mqtt_client_connected=[{}]'.format(mqtt_client_connected), debug=True) sleep(1.0) # some slack to establish the connection - startAliveTimer() + start_alive_timer() sd_notifier.notify('READY=1') @@ -1338,15 +1354,15 @@ def isAliveTimerRunning(): # ----------------------------------------------------------------------------- # what RPi device are we on? -# get our hostnames so we can setup MQTT -getNetworkIFs() # this will fill-in rpi_mac +# get our hostnames so we can set up MQTT +get_network_ifs() # this will fill in rpi_mac mac_basic = rpi_mac.lower().replace(":", "") mac_left = mac_basic[:6] mac_right = mac_basic[6:] print_line('mac lt=[{}], rt=[{}], mac=[{}]'.format( mac_left, mac_right, mac_basic), debug=True) -uniqID = "RPi-{}Mon{}".format(mac_left, mac_right) +uniq_id = "RPi-{}Mon{}".format(mac_left, mac_right) # our RPi Reporter device # KeyError: 'home310/sensor/rpi-pi3plus/values' let's not use this 'values' as topic @@ -1379,7 +1395,7 @@ def isAliveTimerRunning(): # Publish our MQTT auto discovery # table of key items to publish: -detectorValues = OrderedDict([ +detector_values = OrderedDict([ (K_LD_MONITOR, dict( title="RPi Monitor {}".format(rpi_hostname), topic_category="sensor", @@ -1426,7 +1442,7 @@ def isAliveTimerRunning(): ]) for [command, _] in commands.items(): - #print_line('- REGISTER command: [{}]'.format(command), debug=True) + # print_line('- REGISTER command: [{}]'.format(command), debug=True) iconName = 'mdi:gesture-tap' if 'reboot' in command: iconName = 'mdi:restart' @@ -1434,37 +1450,38 @@ def isAliveTimerRunning(): iconName = 'mdi:power-sleep' elif 'service' in command: iconName = 'mdi:cog-counterclockwise' - detectorValues.update({ + detector_values.update({ command: dict( title='RPi {} {} Command'.format(command, rpi_hostname), topic_category='button', no_title_prefix='yes', icon=iconName, - command = command, - command_topic = '{}/{}'.format(command_base_topic, command) + command=command, + command_topic='{}/{}'.format(command_base_topic, command) ) }) -#print_line('- detectorValues=[{}]'.format(detectorValues), debug=True) +# print_line('- detectorValues=[{}]'.format(detectorValues), debug=True) sensor_base_topic = '{}/sensor/{}'.format(base_topic, sensor_name.lower()) values_topic_rel = '{}/{}'.format('~', K_LD_MONITOR) values_topic = '{}/{}'.format(sensor_base_topic, K_LD_MONITOR) -activity_topic_rel = '{}/status'.format('~') # vs. LWT -activity_topic = '{}/status'.format(sensor_base_topic) # vs. LWT +activity_topic_rel = '{}/status'.format('~') # vs. LWT +activity_topic = '{}/status'.format(sensor_base_topic) # vs. LWT command_topic_rel = '~/set' # discovery_topic = '{}/sensor/{}/{}/config'.format(discovery_prefix, sensor_name.lower(), sensor) -for [sensor, params] in detectorValues.items(): - discovery_topic = '{}/{}/{}/{}/config'.format(discovery_prefix, params['topic_category'], sensor_name.lower(), sensor) +for [sensor, params] in detector_values.items(): + discovery_topic = '{}/{}/{}/{}/config'.format(discovery_prefix, params['topic_category'], sensor_name.lower(), + sensor) payload = OrderedDict() if 'no_title_prefix' in params: payload['name'] = "{}".format(params['title'].title()) else: payload['name'] = "{} {}".format( sensor_name.title(), params['title'].title()) - payload['uniq_id'] = "{}_{}".format(uniqID, sensor.lower()) + payload['uniq_id'] = "{}_{}".format(uniq_id, sensor.lower()) if 'device_class' in params: payload['dev_cla'] = params['device_class'] if 'unit' in params: @@ -1492,7 +1509,7 @@ def isAliveTimerRunning(): payload['json_attr_tpl'] = '{{{{ value_json.{} | tojson }}}}'.format(K_LD_PAYLOAD_NAME) if 'device_ident' in params: payload['dev'] = { - 'identifiers': ["{}".format(uniqID)], + 'identifiers': ["{}".format(uniq_id)], 'manufacturer': 'Raspberry Pi (Trading) Ltd.', 'name': params['device_ident'], 'model': '{}'.format(rpi_model), @@ -1500,7 +1517,7 @@ def isAliveTimerRunning(): } else: payload['dev'] = { - 'identifiers': ["{}".format(uniqID)], + 'identifiers': ["{}".format(uniq_id)], } mqtt_client.publish(discovery_topic, json.dumps(payload), 1, retain=True) @@ -1514,42 +1531,39 @@ def isAliveTimerRunning(): TEST_INTERRUPT = (-2) -def periodTimeoutHandler(): +def period_timeout_handler(): print_line('- PERIOD TIMER INTERRUPT -', debug=True) handle_interrupt(TIMER_INTERRUPT) # '0' means we have a timer interrupt!!! - startPeriodTimer() - - -def startPeriodTimer(): - global endPeriodTimer - global periodTimeRunningStatus - stopPeriodTimer() - endPeriodTimer = threading.Timer( - interval_in_minutes * 60.0, periodTimeoutHandler) - endPeriodTimer.start() - periodTimeRunningStatus = True - print_line( - '- started PERIOD timer - every {} seconds'.format(interval_in_minutes * 60.0), debug=True) - - -def stopPeriodTimer(): - global endPeriodTimer - global periodTimeRunningStatus - endPeriodTimer.cancel() - periodTimeRunningStatus = False + start_period_timer() + + +def start_period_timer(): + global end_period_timer + global period_time_running_status + stop_period_timer() + end_period_timer = threading.Timer(interval_in_minutes * 60.0, period_timeout_handler) + end_period_timer.start() + period_time_running_status = True + print_line('- started PERIOD timer - every {} seconds'.format(interval_in_minutes * 60.0), debug=True) + + +def stop_period_timer(): + global end_period_timer + global period_time_running_status + end_period_timer.cancel() + period_time_running_status = False print_line('- stopped PERIOD timer', debug=True) -def isPeriodTimerRunning(): - global periodTimeRunningStatus - return periodTimeRunningStatus +def is_period_timer_running(): + global period_time_running_status + return period_time_running_status # our TIMER -endPeriodTimer = threading.Timer( - interval_in_minutes * 60.0, periodTimeoutHandler) +end_period_timer = threading.Timer(interval_in_minutes * 60.0, period_timeout_handler) # our BOOL tracking state of TIMER -periodTimeRunningStatus = False +period_time_running_status = False reported_first_time = False # ----------------------------------------------------------------------------- @@ -1608,113 +1622,112 @@ def isPeriodTimerRunning(): K_RPI_THROTTLE = "throttle" -def send_status(timestamp, nothing): - rpiData = OrderedDict() - rpiData[SCRIPT_TIMESTAMP] = timestamp.astimezone().replace( - microsecond=0).isoformat() - rpiData[K_RPI_MODEL] = rpi_model - rpiData[K_RPI_CONNECTIONS] = rpi_connections - rpiData[K_RPI_HOSTNAME] = rpi_hostname - rpiData[K_RPI_FQDN] = rpi_fqdn - rpiData[K_RPI_LINUX_RELEASE] = rpi_linux_release - rpiData[K_RPI_LINUX_VERSION] = rpi_linux_version - rpiData[K_RPI_LINUX_AVAIL_UPD] = rpi_update_count - rpiData[K_RPI_UPTIME] = rpi_uptime - rpiData[K_RPI_UPTIME_SECONDS] = rpi_uptime_sec +def send_status(timestamp, _): + rpi_data = OrderedDict() + rpi_data[SCRIPT_TIMESTAMP] = timestamp.astimezone().replace(microsecond=0).isoformat() + rpi_data[K_RPI_MODEL] = rpi_model + rpi_data[K_RPI_CONNECTIONS] = rpi_connections + rpi_data[K_RPI_HOSTNAME] = rpi_hostname + rpi_data[K_RPI_FQDN] = rpi_fqdn + rpi_data[K_RPI_LINUX_RELEASE] = rpi_linux_release + rpi_data[K_RPI_LINUX_VERSION] = rpi_linux_version + rpi_data[K_RPI_LINUX_AVAIL_UPD] = rpi_update_count + rpi_data[K_RPI_UPTIME] = rpi_uptime + rpi_data[K_RPI_UPTIME_SECONDS] = rpi_uptime_sec # DON'T use V1 form of getting date (my dashbord mech) - #actualDate = datetime.strptime(rpi_last_update_date, '%y%m%d%H%M%S') + # actualDate = datetime.strptime(rpi_last_update_date, '%y%m%d%H%M%S') # actualDate.replace(tzinfo=local_tz) - #rpiData[K_RPI_DATE_LAST_UPDATE] = actualDate.astimezone().replace(microsecond=0).isoformat() + # rpi_data[K_RPI_DATE_LAST_UPDATE] = actualDate.astimezone().replace(microsecond=0).isoformat() # also don't use V2 form... # if rpi_last_update_date_v2 != datetime.min: - # rpiData[K_RPI_DATE_LAST_UPDATE] = rpi_last_update_date_v2.astimezone().replace(microsecond=0).isoformat() + # rpi_data[K_RPI_DATE_LAST_UPDATE] = rpi_last_update_date_v2.astimezone().replace(microsecond=0).isoformat() # else: - # rpiData[K_RPI_DATE_LAST_UPDATE] = '' + # rpi_data[K_RPI_DATE_LAST_UPDATE] = '' if rpi_last_update_date != datetime.min: - rpiData[K_RPI_DATE_LAST_UPDATE] = rpi_last_update_date.astimezone().replace( + rpi_data[K_RPI_DATE_LAST_UPDATE] = rpi_last_update_date.astimezone().replace( microsecond=0).isoformat() else: - rpiData[K_RPI_DATE_LAST_UPDATE] = '' - rpiData[K_RPI_FS_SPACE] = int(rpi_filesystem_space.replace('GB', ''), 10) + rpi_data[K_RPI_DATE_LAST_UPDATE] = '' + rpi_data[K_RPI_FS_SPACE] = int(rpi_filesystem_space.replace('GB', ''), 10) # TODO: consider eliminating K_RPI_FS_AVAIL/fs_free_prcnt as used is needed but free is not... (can be calculated) - rpiData[K_RPI_FS_AVAIL] = 100 - int(rpi_filesystem_percent, 10) - rpiData[K_RPI_FS_USED] = int(rpi_filesystem_percent, 10) + rpi_data[K_RPI_FS_AVAIL] = 100 - int(rpi_filesystem_percent, 10) + rpi_data[K_RPI_FS_USED] = int(rpi_filesystem_percent, 10) - rpiData[K_RPI_NETWORK] = getNetworkDictionary() + rpi_data[K_RPI_NETWORK] = get_network_dictionary() - rpiDrives = getDrivesDictionary() - if len(rpiDrives) > 0: - rpiData[K_RPI_DRIVES] = rpiDrives + rpi_drives = get_drives_dictionary() + if len(rpi_drives) > 0: + rpi_data[K_RPI_DRIVES] = rpi_drives - rpiRam = getMemoryDictionary() - if len(rpiRam) > 0: - rpiData[K_RPI_MEMORY] = rpiRam - ramSizeMB = int('{:.0f}'.format(rpi_memory_tuple[0], 10)) # "mem_space_mbytes" + rpi_ram = get_memory_dictionary() + if len(rpi_ram) > 0: + rpi_data[K_RPI_MEMORY] = rpi_ram + ram_size_mb = int('{:.0f}'.format(rpi_memory_tuple[0], 10)) # "mem_space_mbytes" # used is total - free - ramUsedMB = int('{:.0f}'.format(rpi_memory_tuple[0] - rpi_memory_tuple[2]), 10) - ramUsedPercent = int((ramUsedMB / ramSizeMB) * 100) - rpiData[K_RPI_RAM_USED] = ramUsedPercent # "mem_used_prcnt" + ram_used_mb = int('{:.0f}'.format(rpi_memory_tuple[0] - rpi_memory_tuple[2]), 10) + ram_used_percent = int((ram_used_mb / ram_size_mb) * 100) + rpi_data[K_RPI_RAM_USED] = ram_used_percent # "mem_used_prcnt" - rpiCpu = getCPUDictionary() - if len(rpiCpu) > 0: - rpiData[K_RPI_CPU] = rpiCpu + rpi_cpu = get_cpu_dictionary() + if len(rpi_cpu) > 0: + rpi_data[K_RPI_CPU] = rpi_cpu if len(rpi_throttle_status) > 0: - rpiData[K_RPI_THROTTLE] = rpi_throttle_status + rpi_data[K_RPI_THROTTLE] = rpi_throttle_status - rpiData[K_RPI_SYSTEM_TEMP] = forceSingleDigit(rpi_system_temp) - rpiData[K_RPI_GPU_TEMP] = forceSingleDigit(rpi_gpu_temp) - rpiData[K_RPI_CPU_TEMP] = forceSingleDigit(rpi_cpu_temp) + rpi_data[K_RPI_SYSTEM_TEMP] = force_single_digit(rpi_system_temp) + rpi_data[K_RPI_GPU_TEMP] = force_single_digit(rpi_gpu_temp) + rpi_data[K_RPI_CPU_TEMP] = force_single_digit(rpi_cpu_temp) - rpiData[K_RPI_SCRIPT] = rpi_mqtt_script.replace('.py', '') - rpiData[K_RPI_SCRIPT_VERSIONS] = ','.join(daemon_version_list) - rpiData[SCRIPT_REPORT_INTERVAL] = interval_in_minutes + rpi_data[K_RPI_SCRIPT] = rpi_mqtt_script.replace('.py', '') + rpi_data[K_RPI_SCRIPT_VERSIONS] = ','.join(daemon_version_list) + rpi_data[SCRIPT_REPORT_INTERVAL] = interval_in_minutes - rpiTopDict = OrderedDict() - rpiTopDict[K_LD_PAYLOAD_NAME] = rpiData + rpi_top_dict = OrderedDict() + rpi_top_dict[K_LD_PAYLOAD_NAME] = rpi_data - _thread.start_new_thread(publishMonitorData, (rpiTopDict, values_topic)) + threading.Thread(target=publish_monitor_data, args=(rpi_top_dict, values_topic)).start() -def forceSingleDigit(temperature): - tempInterp = '{:.1f}'.format(temperature) - return float(tempInterp) +def force_single_digit(temperature): + temp_interp = '{:.1f}'.format(temperature) + return float(temp_interp) -def getDrivesDictionary(): +def get_drives_dictionary(): global rpi_filesystem - rpiDrives = OrderedDict() + rpi_drives = OrderedDict() # tuple { total blocks, used%, mountPoint, device } - for driveTuple in rpi_filesystem: - rpiSingleDrive = OrderedDict() - rpiSingleDrive[K_RPI_DRV_BLOCKS] = int(driveTuple[0]) - rpiSingleDrive[K_RPI_DRV_USED] = int(driveTuple[1]) - device = driveTuple[3] + for drive_tuple in rpi_filesystem: + rpi_single_drive = OrderedDict() + rpi_single_drive[K_RPI_DRV_BLOCKS] = int(drive_tuple[0]) + rpi_single_drive[K_RPI_DRV_USED] = int(drive_tuple[1]) + device = drive_tuple[3] if ':' in device: - rpiDevice = OrderedDict() - lineParts = device.split(':') - rpiDevice[K_RPI_DVC_IP] = lineParts[0] - rpiDevice[K_RPI_DVC_PATH] = lineParts[1] - rpiSingleDrive[K_RPI_DRV_NFS] = rpiDevice + rpi_device = OrderedDict() + line_parts = device.split(':') + rpi_device[K_RPI_DVC_IP] = line_parts[0] + rpi_device[K_RPI_DVC_PATH] = line_parts[1] + rpi_single_drive[K_RPI_DRV_NFS] = rpi_device else: - rpiSingleDrive[K_RPI_DRV_DEVICE] = device - #rpiTest = OrderedDict() - #rpiTest[K_RPI_DVC_IP] = '255.255.255.255' - #rpiTest[K_RPI_DVC_PATH] = '/srv/c2db7b94' - #rpiSingleDrive[K_RPI_DRV_NFS] = rpiTest - rpiSingleDrive[K_RPI_DRV_MOUNT] = driveTuple[2] - driveKey = driveTuple[2].replace('/', '-').replace('-', '', 1) - if len(driveKey) == 0: - driveKey = "root" - rpiDrives[driveKey] = rpiSingleDrive + rpi_single_drive[K_RPI_DRV_DEVICE] = device + # rpiTest = OrderedDict() + # rpiTest[K_RPI_DVC_IP] = '255.255.255.255' + # rpiTest[K_RPI_DVC_PATH] = '/srv/c2db7b94' + # rpi_single_drive[K_RPI_DRV_NFS] = rpiTest + rpi_single_drive[K_RPI_DRV_MOUNT] = drive_tuple[2] + drive_key = drive_tuple[2].replace('/', '-').replace('-', '', 1) + if len(drive_key) == 0: + drive_key = "root" + rpi_drives[drive_key] = rpi_single_drive # TEST NFS - return rpiDrives + return rpi_drives -def getNetworkDictionary(): +def get_network_dictionary(): global rpi_interfaces # TYPICAL: # rpi_interfaces=[[ @@ -1722,125 +1735,128 @@ def getNetworkDictionary(): # ('wlan0', 'IP', '192.168.100.189'), # ('wlan0', 'mac', 'b8:27:eb:4f:a6:e9') # ]] - networkData = OrderedDict() + network_data = OrderedDict() - priorIFKey = '' - tmpData = OrderedDict() + prior_if_key = '' + tmp_data = OrderedDict() for currTuple in rpi_interfaces: - currIFKey = currTuple[0] - if priorIFKey == '': - priorIFKey = currIFKey - if currIFKey != priorIFKey: + curr_if_key = currTuple[0] + if prior_if_key == '': + prior_if_key = curr_if_key + if curr_if_key != prior_if_key: # save off prior if exists - if priorIFKey != '': - networkData[priorIFKey] = tmpData - tmpData = OrderedDict() - priorIFKey = currIFKey - subKey = currTuple[1] - subValue = currTuple[2] - tmpData[subKey] = subValue - networkData[priorIFKey] = tmpData - print_line('networkData:{}"'.format(networkData), debug=True) - return networkData - - -def getMemoryDictionary(): + if prior_if_key != '': + network_data[prior_if_key] = tmp_data + tmp_data = OrderedDict() + prior_if_key = curr_if_key + sub_key = currTuple[1] + sub_value = currTuple[2] + tmp_data[sub_key] = sub_value + network_data[prior_if_key] = tmp_data + print_line('network_data:{}"'.format(network_data), debug=True) + return network_data + + +def get_memory_dictionary(): # TYPICAL: # Tuple (Total, Free, Avail.) - memoryData = OrderedDict() - if rpi_memory_tuple != '': + memory_data = OrderedDict() + if rpi_memory_tuple: # TODO: remove free fr - memoryData[K_RPI_MEM_TOTAL] = round(rpi_memory_tuple[0]) - memoryData[K_RPI_MEM_FREE] = round(rpi_memory_tuple[2]) - memoryData[K_RPI_SWAP_TOTAL] = round(rpi_memory_tuple[3]) - memoryData[K_RPI_SWAP_FREE] = round(rpi_memory_tuple[4]) - #print_line('memoryData:{}"'.format(memoryData), debug=True) - return memoryData + memory_data[K_RPI_MEM_TOTAL] = round(rpi_memory_tuple[0]) + memory_data[K_RPI_MEM_FREE] = round(rpi_memory_tuple[2]) + memory_data[K_RPI_SWAP_TOTAL] = round(rpi_memory_tuple[3]) + memory_data[K_RPI_SWAP_FREE] = round(rpi_memory_tuple[4]) + # print_line('memory_data:{}"'.format(memory_data), debug=True) + return memory_data -def getCPUDictionary(): +def get_cpu_dictionary(): # TYPICAL: # Tuple (Hardware, Model Name, NbrCores, BogoMIPS, Serial) - cpuDict = OrderedDict() - #print_line('rpi_cpu_tuple:{}"'.format(rpi_cpu_tuple), debug=True) + cpu_dict = OrderedDict() + # print_line('rpi_cpu_tuple:{}"'.format(rpi_cpu_tuple), debug=True) if rpi_cpu_tuple != '': - cpuDict[K_RPI_CPU_HARDWARE] = rpi_cpu_tuple[0] - cpuDict[K_RPI_CPU_MODEL] = rpi_cpu_tuple[1] - cpuDict[K_RPI_CPU_CORES] = rpi_cpu_tuple[2] - cpuDict[K_RPI_CPU_BOGOMIPS] = '{:.2f}'.format(rpi_cpu_tuple[3]) - cpuDict[K_RPI_CPU_SERIAL] = rpi_cpu_tuple[4] - cpuDict[K_RPI_CPU_LOAD1] = rpi_cpu_tuple[5] - cpuDict[K_RPI_CPU_LOAD5] = rpi_cpu_tuple[6] - cpuDict[K_RPI_CPU_LOAD15] = rpi_cpu_tuple[7] - print_line('cpuDict:{}"'.format(cpuDict), debug=True) - return cpuDict - - -def publishMonitorData(latestData, topic): + cpu_dict[K_RPI_CPU_HARDWARE] = rpi_cpu_tuple[0] + cpu_dict[K_RPI_CPU_MODEL] = rpi_cpu_tuple[1] + cpu_dict[K_RPI_CPU_CORES] = rpi_cpu_tuple[2] + cpu_dict[K_RPI_CPU_BOGOMIPS] = '{:.2f}'.format(rpi_cpu_tuple[3]) + cpu_dict[K_RPI_CPU_SERIAL] = rpi_cpu_tuple[4] + cpu_dict[K_RPI_CPU_LOAD1] = rpi_cpu_tuple[5] + cpu_dict[K_RPI_CPU_LOAD5] = rpi_cpu_tuple[6] + cpu_dict[K_RPI_CPU_LOAD15] = rpi_cpu_tuple[7] + print_line('cpu_dict:{}"'.format(cpu_dict), debug=True) + return cpu_dict + + +def publish_monitor_data(latest_data, topic): print_line('Publishing to MQTT topic "{}, Data:{}"'.format( - topic, json.dumps(latestData))) + topic, json.dumps(latest_data))) mqtt_client.publish('{}'.format(topic), json.dumps( - latestData), 1, retain=False) + latest_data), 1, retain=False) sleep(0.5) # some slack for the publish roundtrip and callback function def update_values(): # run get latest values for all - getDeviceCpuInfo() - getUptime() - getFileSystemDrives() - getSystemTemperature() - getSystemThermalStatus() - getLastUpdateDate() - getDeviceMemory() - getNetworkIFs() + get_device_cpu_info() + get_uptime() + get_file_system_drives() + get_system_temperature() + get_system_thermal_status() + get_last_update_date() + get_device_memory() + get_network_ifs() -# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # Interrupt handler +# ----------------------------------------------------------------------------- def handle_interrupt(channel): global reported_first_time - sourceID = "<< INTR(" + str(channel) + ")" + source_id = "<< INTR(" + str(channel) + ")" current_timestamp = datetime.now(local_tz) - print_line(sourceID + " >> Time to report! (%s)" % + print_line(source_id + " >> Time to report! (%s)" % current_timestamp.strftime('%H:%M:%S - %Y/%m/%d'), verbose=True) # ---------------------------------- # have PERIOD interrupt! update_values() - if (opt_stall == False or reported_first_time == False and opt_stall == True): + if opt_stall is False or reported_first_time is False and opt_stall is True: # ok, report our new detection to MQTT - _thread.start_new_thread(send_status, (current_timestamp, '')) + threading.Thread(target=send_status, args=(current_timestamp, '')).start() + reported_first_time = True else: - print_line(sourceID + " >> Time to report! (%s) but SKIPPED (TEST: stall)" % + print_line(source_id + " >> Time to report! (%s) but SKIPPED (TEST: stall)" % current_timestamp.strftime('%H:%M:%S - %Y/%m/%d'), verbose=True) -def afterMQTTConnect(): +def after_mqtt_connect(): print_line('* afterMQTTConnect()', verbose=True) # NOTE: this is run after MQTT connects # start our interval timer - startPeriodTimer() + start_period_timer() # do our first report handle_interrupt(0) -# TESTING AGAIN -# getNetworkIFs() -# getLastUpdateDate() +# TESTING AGAIN +# get_network_ifs() +# get_last_update_date() +# # TESTING, early abort -# stopAliveTimer() +# stop_alive_timer() # exit(0) -afterMQTTConnect() # now instead of after? +after_mqtt_connect() # now instead of after? # check every 12 hours (twice a day) = 12 hours * 60 minutes * 60 seconds -kVersionCheckIntervalInSeconds = (12 * 60 * 60) +k_version_check_interval_in_seconds = (12 * 60 * 60) # check every 4 hours (6 times a day) = 4 hours * 60 minutes * 60 seconds -kUpdateCheckIntervalInSeconds = (check_interval_in_hours * 60 * 60) +k_update_check_interval_in_seconds = (check_interval_in_hours * 60 * 60) # now just hang in forever loop until script is stopped externally try: @@ -1848,18 +1864,18 @@ def afterMQTTConnect(): # our INTERVAL timer does the work sleep(10000) - timeNow = time.time() - if timeNow > daemon_last_fetch_time + kVersionCheckIntervalInSeconds: - getDaemonReleases() # and load them! + time_now = time() + if time_now > daemon_last_fetch_time + k_version_check_interval_in_seconds: + get_daemon_releases() # and load them! if apt_available: - if timeNow > update_last_fetch_time + kUpdateCheckIntervalInSeconds: - getNumberOfAvailableUpdates() # and count them! + if time_now > update_last_fetch_time + k_update_check_interval_in_seconds: + get_number_of_available_updates() # and count them! finally: # cleanup used pins... just because we like cleaning up after us - publishShuttingDownStatus() - stopPeriodTimer() # don't leave our timers running! - stopAliveTimer() + publish_shutting_down_status() + stop_period_timer() # don't leave our timers running! + stop_alive_timer() mqtt_client.disconnect() print_line('* MQTT Disconnect()', verbose=True) From 9025625168d5e58564955bd79a110804a792019e Mon Sep 17 00:00:00 2001 From: Robert Habermann Date: Mon, 8 Jan 2024 20:14:19 +0100 Subject: [PATCH 2/3] fix get_shell_cmd --- ISP-RPi-mqtt-daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ISP-RPi-mqtt-daemon.py b/ISP-RPi-mqtt-daemon.py index 1b70157..597514f 100755 --- a/ISP-RPi-mqtt-daemon.py +++ b/ISP-RPi-mqtt-daemon.py @@ -961,7 +961,7 @@ def get_shell_cmd(): if os.path.exists(cmd_locn1): desired_command = cmd_locn1 elif os.path.exists(cmd_locn2): - desired_command = '' + desired_command = cmd_locn2 else: print_line('ERROR: sh(1) not found!', error=True) From 0b252b8ad0ef1b369e37aa622485ed7099c302d0 Mon Sep 17 00:00:00 2001 From: Robert Habermann Date: Sat, 24 Feb 2024 22:20:01 +0100 Subject: [PATCH 3/3] ensure backward/forward compatiblity --- ISP-RPi-mqtt-daemon.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ISP-RPi-mqtt-daemon.py b/ISP-RPi-mqtt-daemon.py index 597514f..e0fc42a 100755 --- a/ISP-RPi-mqtt-daemon.py +++ b/ISP-RPi-mqtt-daemon.py @@ -1299,7 +1299,13 @@ def is_alive_timer_running(): lwt_offline_val = 'offline' print_line('Connecting to MQTT broker ...', verbose=True) -mqtt_client = mqtt.Client() +# ensure backward compatibility with older versions of paho-mqtt (<=2.0.0) +# ToDo: Need to update to VERSION2 at some point +try: + mqtt_client = mqtt.Client(callback_api_version=mqtt.CallbackAPIVersion.VERSION1) +except AttributeError: + mqtt_client = mqtt.Client() + # hook up MQTT callbacks mqtt_client.on_connect = on_connect mqtt_client.on_disconnect = on_disconnect