From ca19181fc3780e0522e77703617b6e99a37f9fb9 Mon Sep 17 00:00:00 2001 From: lbussy Date: Thu, 24 Dec 2020 09:38:36 -0600 Subject: [PATCH 01/57] Address syntax error #156 --- brewpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewpi.py b/brewpi.py index f545b63..ebeca03 100755 --- a/brewpi.py +++ b/brewpi.py @@ -1072,7 +1072,7 @@ def loop(): # Main program loop else: phpConn.send( "Profile successfully updated.".encode('utf-8')) - if cs['mode'] is not 'p': + if cs['mode'] != 'p': cs['mode'] = 'p' bgSerialConn.write("j{mode:\"p\"}") logMessage("Profile mode enabled.") From b365f2fba1d6eb20cbc4ae5c0259113e73e94b7c Mon Sep 17 00:00:00 2001 From: lbussy Date: Fri, 25 Dec 2020 06:43:08 -0600 Subject: [PATCH 02/57] Change default log level #158 --- settings/config.cfg.example | 7 +++++++ settings/defaults.cfg | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/settings/config.cfg.example b/settings/config.cfg.example index c523fd0..43d565c 100644 --- a/settings/config.cfg.example +++ b/settings/config.cfg.example @@ -9,6 +9,13 @@ # startupDelay = 1.0 # debug = true # tiltColor = Purple + +# Log JSON: +# This controls logging to the stdout.txt log, as well as the relative +# length and verbosity of the messages. +# +# True = Full logging. May grow very quickly. +# False = Terse messages, only a notice. This is the new default. # logJson = True # On a standard pi installation, the defaults should work. diff --git a/settings/defaults.cfg b/settings/defaults.cfg index 33fc98e..6a8cec8 100644 --- a/settings/defaults.cfg +++ b/settings/defaults.cfg @@ -10,4 +10,4 @@ boardType = arduino beerName = My BrewPi Remix Run interval = 120.0 dataLogging = active -logJson = True +logJson = False From 79dca0dcf50662a51e0c942249abd05596312cb8 Mon Sep 17 00:00:00 2001 From: lbussy Date: Fri, 25 Dec 2020 10:35:51 -0600 Subject: [PATCH 03/57] Fix typo --- brewpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewpi.py b/brewpi.py index ebeca03..6518359 100755 --- a/brewpi.py +++ b/brewpi.py @@ -1840,7 +1840,7 @@ def main(): startSerial() # Begin serial connections loop() # Main processing loop - shutdown() # Process gracefull shutdown + shutdown() # Process graceful shutdown logMessage("Exiting.") From 78c2de0a98afa55010a765a6ad19676e7879048a Mon Sep 17 00:00:00 2001 From: lbussy Date: Sat, 26 Dec 2020 14:36:27 -0600 Subject: [PATCH 04/57] Move to venv and main aio --- .gitignore | 2 +- Tilt.py | 411 ++++++++++++++++++++------------------------- brewpi.py | 2 +- requirements.txt | 8 + utils/doBrewPi.sh | 8 +- utils/doDepends.sh | 87 +++++----- 6 files changed, 248 insertions(+), 270 deletions(-) create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 5f21024..5be807c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Home dir files .bash_logout +.bash_aliases .bashrc .profile .bash_history @@ -9,7 +10,6 @@ brewpi-env/ coverage.xml start.bat sftp-config.json -requirements.txt !.gitignore .idea/ utils/downloads/ diff --git a/Tilt.py b/Tilt.py index 551271b..d1da3ef 100755 --- a/Tilt.py +++ b/Tilt.py @@ -1,52 +1,61 @@ #!/usr/bin/env python3 -# Requires the following apt packages: -# libatlas-base-dev (for numpy) -# Requires the following pip packages: -# aioblescan, numpy (for calibrations) -# -# Requires setcap and bluetooth group membership in order to run without root: -# sudo setcap cap_net_raw+eip $(eval readlink -f `which python3`) +# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) -# TODO: -# I still have no idea why this will not work in a venv -# Fix design version (v1, 2, 3) -# Fix battery value based on version and gattool? -# Change tilt manager object to an array even if one color +# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX). +# +# BrewPi Script RMX is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# BrewPi Script RMX is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BrewPi Script RMX. If not, see . -import sys -from os.path import dirname, abspath, exists, isfile, getmtime -from csv import reader -from time import time, sleep -from configparser import ConfigParser -import subprocess -import asyncio +# Imports sorted by isort +# +# System Imports import argparse -import re +import asyncio import datetime +from decimal import * +import json +import re +import subprocess +import sys import threading -import aioblescan +from configparser import ConfigParser +from csv import reader +from os.path import abspath, dirname, exists, getmtime, isfile from struct import unpack -import json +from time import sleep, time +# Third Party Imports import numpy +import aioblescan +# Local Imports +# -# DEBUG: -# import sentry_sdk -# sentry_sdk.init("https://5644cfdc9bd24dfbaadea6bc867a8f5b@sentry.io/1803681") - -# A list of all possible Tilt colors. +# Tilt colors TILT_COLORS = ['Red', 'Green', 'Black', 'Purple', 'Orange', 'Blue', 'Yellow', 'Pink'] +# Known Tilt hardware versions TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] -class TiltManager: +class TiltManager(object): """ - Manages the monitoring of all Tilts and storing the read values + Class defining the content of a Tilt advertisement. Manages the monitoring + of all Tilts and storing the read values """ + TILT = 'a495' threads = [] - def __init__(self, color=None, averagingPeriod=0, medianWindow=0, dev_id=0): + def __init__(self, averagingPeriod=0, medianWindow=0, dev_id=0): """ Initializes TiltManager class with default values @@ -57,26 +66,29 @@ def __init__(self, color=None, averagingPeriod=0, medianWindow=0, dev_id=0): """ self.tilt = None - self.color = color + self.opts = None + self.debug = False self.dev_id = dev_id self.averagingPeriod = averagingPeriod self.medianWindow = medianWindow - if color is None: - # Set up an array of Tilt objects, one for each color - self.tilt = [None] * len(TILT_COLORS) - for i in range(len(TILT_COLORS)): - self.tilt[i] = Tilt( - TILT_COLORS[i], averagingPeriod, medianWindow) - else: - # Set up a single Tilt object - self.tilt = Tilt(color, averagingPeriod, medianWindow) + + # Set up an array of Tilt objects, one for each color + self.tilt = [None] * len(TILT_COLORS) + for i in range(len(TILT_COLORS)): + self.tilt[i] = Tilt( + TILT_COLORS[i], averagingPeriod, medianWindow) + self.conn = None self.btctrl = None self.event_loop = None self.mysocket = None self.fac = None - self.tiltError = False + def setDebug(self, debug): + self.debug = debug + + def setOpts(self, opts): + self.opts = opts def loadSettings(self): """ @@ -177,131 +189,41 @@ def getValue(self, color): returnValue = self.tilt.getValues(color) return returnValue - def decode(self, packet): + def decode(self, ev): """ - Format Tilt values. - Tilt format based on iBeacon format and filter includes Apple iBeacon - identifier portion (4c000215) as well as Tilt specific uuid preamble - (a495). + Decode BLEacon to Tilt data - :param packet: Raw BLEacon packet - :return: Tilt values encoded as JSON + :param ev: Pointer to aioblescan.HCI_Event() + :return: Tilt values in JSON """ - # The Tilt format is based on the iBeacon format, and the filter value includes - # the Apple iBeacon identifier portion (4c000215) as well as the Tilt specific - # uuid preamble (a495). - TILT = '4c000215a495' - - # The first reference I recall seeing on this format is here: - # https://kvurd.com/blog/tilt-hydrometer-ibeacon-data-format/ - # - # Importantly: - # - # Example tilt hydrometer sensor data message from hcidump -R: - # - # > 04 3E 27 02 01 00 00 5A 09 9B 16 A3 04 1B 1A FF 4C 00 02 15 - # A4 95 BB 10 C5 B1 4B 44 B5 12 13 70 F0 2D 74 DE 00 44 03 F8 - # C5 C7 - # - # Explanation (with help from the bluetooth core spec and stackoverflow [4] [5] [6]): - # - # 04: HCI Packet Type HCI Event - # 3E: LE Meta event - # 27: Parameter total length (39 octets) - # 02: LE Advertising report sub-event - # 01: Number of reports (1) - # 00: Event type connectable and scannable undirected advertising - # 00: Public address type - # 5A: address - # 09: address - # 9B: address - # 16: address - # A3: address - # 04: address - # 1B: length of data field (27 octets) - # 1A: length of first advertising data (AD) structure (26) - # FF: type of first AD structure - manufacturer specific data - # 4C: manufacturer ID - Apple iBeacon <- *This is where we start checking for a Tilt - # 00: manufacturer ID - Apple iBeacon - # 02: type (constant, defined by iBeacon spec) - # 15: length (constant, defined by iBeacon spec) - # A4: device UUID - # 95: device UUID < - This is where we stop checking for a Tilt - # BB: device UUID - # 10: device UUID < - Color, 10 - 80 - # C5: device UUID - # B1: device UUID - # 4B: device UUID - # 44: device UUID - # B5: device UUID - # 12: device UUID - # 13: device UUID - # 70: device UUID - # F0: device UUID - # 2D: device UUID - # 74: device UUID - # DE: device UUID - # 00: 'major' field of iBeacon data - temperature (in degrees fahrenheit) - # 44: 'major' field of iBeacon data - temperature (in degrees fahrenheit) - # 03: 'minor' field of iBeacon data - specific gravity (x1000) - # F8: 'minor' field of iBeacon data - specific gravity (x1000) - # C5: The TX power in dBm is a signed 8 bit integer. (-59dBm above or 197 unsigned) - # C7: Received signal strength indication (RSSI) is a signed 8 bit integer (-57dBm above or 199 unsigned) - # - # Temperature is a 16 bit unsigned integer, most significant bits first (big endian). - # - # The specific gravity x 1000 (‘minor’ field of iBeacon data) is a 16 bit unsigned integer, most significant bits first (big endian). Divide by 1000 to get the specific gravity. - # - # The UUID of the Tilt Hydrometer is shared between devices of that colour. The list is as follows [7]: - # - # Red: A495BB10C5B14B44B5121370F02D74DE - # Green: A495BB20C5B14B44B5121370F02D74DE - # Black: A495BB30C5B14B44B5121370F02D74DE - # Purple: A495BB40C5B14B44B5121370F02D74DE - # Orange: A495BB50C5B14B44B5121370F02D74DE - # Blue: A495BB60C5B14B44B5121370F02D74DE - # Yellow: A495BB70C5B14B44B5121370F02D74DE - # Pink: A495BB80C5B14B44B5121370F02D74DE - data = {} - raw_data = packet.retrieve('Payload for mfg_specific_data') - ev_type = packet.retrieve('ev type') - msd = packet.retrieve('Manufacturer Specific Data') - if raw_data: - pckt = raw_data[0].val - payload = raw_data[0].val.hex() - mfg_id = payload[0:12] - rssi = packet.retrieve('rssi') - mac = packet.retrieve('peer') - if mfg_id == TILT: - - # packet.show(0) # DEBUG - # "ev type" - # 0:"generic adv" (Tilt v1) - # 3:"no connection adv" (Tilt v2, 3 and Pro) - # 4:"scan rsp" - data['ev_type'] = ev_type[0].val - - data['uuid'] = payload[8:40] - # Temperature (in degrees fahrenheit) - data['major'] = unpack('>H', pckt[20:22])[0] - data['minor'] = unpack('>H', pckt[22:24])[0] # Specific gravity (x1000) - # tx_power will be -59 every 5 seconds in order to allow iOS - # to compute RSSI correctly. Only use 0 or real value. - data['tx_power'] = unpack('>b', pckt[24:25])[0] - data['rssi'] = rssi[-1].val - data['mac'] = mac[-1].val - return json.dumps(data).encode('utf-8') - else: - return None + manufacturer_data = ev.retrieve("Manufacturer Specific Data") + if manufacturer_data: + payload = manufacturer_data[0].payload + payload = payload[1].val.hex() + if payload[4:8] == self.TILT: + data['mfg_id'] = payload[0:8] + data['color'] = self.tiltName(payload[4:36]) + data['mac'] = ev.retrieve("peer")[0].val + data['uuid'] = payload[0:-1] + data['major'] = int.from_bytes(bytes.fromhex(payload[36:40]), byteorder='big') + data['minor'] = int.from_bytes(bytes.fromhex(payload[40:44]), byteorder='big') + # On the latest tilts, TX power is used for battery age in + # weeks since battery change (0-152 when converted to unsigned + # 8 bit integer) and other TBD operation codes + data['tx_power'] = int.from_bytes(bytes.fromhex(payload[44:46]), byteorder='big', signed=False) + data['rssi'] = ev.retrieve("rssi")[-1].val + data['ev_type'] = int(ev.retrieve('ev type')[-1].val) + + return json.dumps(data) def blecallback(self, data): """ - Callback method for the Bluetooth process - In turn calls self.decode() and then self.storeValue() + Callback method for aioblescan. In turn calls self.decode() and + then self.storeValue(). - :param data: Data from aioblescan + :param data: Data from aioblescan.BLEScanRequester :return: None """ @@ -309,56 +231,87 @@ def blecallback(self, data): temperature = 68 gravity = 1 - packet = aioblescan.HCI_Event() - packet.decode(data) - # packet.show(0) # DEBUG - response = self.decode(packet) - - if response: - tiltdata = json.loads(response.decode('utf-8', 'ignore')) - - if self.color == None or self.tiltName(tiltdata['uuid']) == self.color: - mac = str(tiltdata['mac']) - color = self.tiltName(tiltdata['uuid']) - - if int(tiltdata['major']) == 999: - # For the latest Tilts, this is now actually a special code indicating that - # the gravity is the version info. - fwVersion = int(tiltdata['minor']) - else: - if int(tiltdata['minor']) >= 5000: - # Is a Tilt Pro - # self.tilt_pro = True - gravity = float(tiltdata['minor']) / 10000 - temperature = float(tiltdata['major']) / 10 - else: - # Is not a Pro model - gravity = float(tiltdata['minor']) / 1000 - temperature = float(tiltdata['major']) + ev = aioblescan.HCI_Event() + ev.decode(data) + tiltJson = self.decode(ev) + + display_raw = True + if tiltJson: + if self.opts: + if self.opts.mac: # Limit to MAC in argument + goon = False + mac = ev.retrieve("peer") + for x in mac: + if x.val in self.opts.mac: + goon =True + break + if not goon: + return + + if self.opts.raw and self.debug: # Print Debug + display_raw = False + print("Raw data: {}".format(ev.raw_data)) + + if self.opts.json and self.debug: # Print Debug + display_raw = False + print("Tilt JSON: {}".format(tiltJson)) + + else: # Not a Tilt + return + + if display_raw and self.debug: # Print Debug + ev.show(0) + + # Prepare to store Tilt data + tiltData = {} + tiltdata = json.loads(tiltJson) + + mac = str(tiltdata['mac']) + color = str(tiltdata['color']) + + if int(tiltdata['major']) == 999: + # For the latest Tilts, this is now actually a special code indicating that + # the gravity is the version info. + fwVersion = int(tiltdata['minor']) + else: + if int(tiltdata['minor']) >= 5000: + # Is a Tilt Pro + # self.tilt_pro = True + gravity = Decimal(tiltdata['minor']) / 10000 + temperature = Decimal(tiltdata['major']) / 10 + else: + # Is not a Pro model + gravity = Decimal(tiltdata['minor']) / 1000 + temperature = int(tiltdata['major']) - battery = int(tiltdata['tx_power']) + battery = int(tiltdata['tx_power']) - # Try to derive if we are v1, 2, or 3 - if int(tiltdata['ev_type']) == 0: # Only Tilt v1 shows as "generic adv" - hwVersion = 1 - elif int(tiltdata['minor']) >= 5000: - hwVersion = 4 - else: # TODO: 5 is "v2 or 3" until we can tell the difference between the two of them - hwVersion = 5 + # Try to derive if we are v1, 2, or 3 + if int(tiltdata['ev_type']) == 0: # Only Tilt v1 shows as "generic adv" + hwVersion = 1 + elif int(tiltdata['minor']) >= 5000: + hwVersion = 4 + else: # TODO: 5 is "v2 or 3" until we can tell the difference between the two of them + hwVersion = 5 - rssi = int(tiltdata['rssi']) + rssi = tiltdata['rssi'] - timestamp = datetime.datetime.now() + timestamp = datetime.datetime.now() - self.storeValue(timestamp, mac, hwVersion, fwVersion, color, temperature, gravity, battery) + # Store value + timestamp = datetime.datetime.now() + self.storeValue(timestamp, mac, hwVersion, fwVersion, color, temperature, gravity, battery) - def start(self): + def start(self, debug = False): """ Starts the BLE scanning thread :return: None """ + # Turn debug printing on or off + self.setDebug(debug) + self.event_loop = asyncio.get_event_loop() # First create and configure a raw socket self.mysocket = aioblescan.create_bt_socket(self.dev_id) @@ -653,19 +606,22 @@ def getBatteryValue(self, color): batteryValues = [] for i in range(len(self.values)): value = self.values[i] - batteryValues.append(value.battery) + if not value.battery == 197: # Skip -59 (197 unsigned) + batteryValues.append(value.battery) # Since tx_power will be -59 every 5 seconds in order to allow iOS # to compute RSSI correctly, we cache the last good value and only # use the max of all on-hand values as the battery age to prevent # zeroes. A zero value should only come from V1 (and maybe v2) Tilts. - batteryValue = max(batteryValues) + batteryValue = 0 + if len(batteryValues): + batteryValue = max(batteryValues) self.lastBatt = max(batteryValue, self.lastBatt) return self.lastBatt def getHwVersion(self, color): """ - Return HArdware Version for a given color + Return Hardware Version for a given color :param values: An array of Tilt values :return: Int of TILT_VERSIONS or empty string @@ -772,8 +728,8 @@ def tiltCal(self, which, color): for row in csvFileReader: # Skip any blank or comment rows if row != [] and row[0][:1] != "#": - originalValues.append(float(row[0])) - actualValues.append(float(row[1])) + originalValues.append(Decimal(row[0])) + actualValues.append(Decimal(row[1])) # Close file csvFile.close() except IOError: @@ -817,7 +773,6 @@ def check_mac(mac): pass raise argparse.ArgumentTypeError("{}} is not a MAC address".format(mac)) - def parseArgs(): """ Parse any command line arguments @@ -840,6 +795,12 @@ def parseArgs(): action='store_true', default=False, help="display Tilt data in JSON format") + parser.add_argument( + "-v", + "--verbose", + action='store_true', + default=False, + help="display debug data") parser.add_argument( "-m", "--mac", @@ -848,40 +809,39 @@ def parseArgs(): help="filter Tilts by this/these MAC address(es)") parser.add_argument( "-d", - "--hci", + "--dev", type=int, default=0, help="select the hci device to use (default 0, i.e. hci0)") parser.add_argument( "-c", - "--color", + "--col", type=str, default=None, help="filter by this Tilt color") parser.add_argument( "-a", - "--average", + "--avg", type=int, default=None, help="seconds window for averaging") parser.add_argument( "-n", - "--median", + "--med", type=int, default=None, help="number of entries in median window") try: opts = parser.parse_args() - opts.color = opts.color.title() if opts.color else None - if opts.color and opts.color not in TILT_COLORS: + opts.col = opts.col.title() if opts.col else None + if opts.col and opts.col not in TILT_COLORS: parser.error("Invalid color choice.") - opts.hci = opts.hci if opts.hci else 0 + opts.dev = opts.dev if opts.dev else 0 return opts except Exception as e: parser.error("Error: " + str(e)) sys.exit() - def checkSetcap() -> (bool, str, str): """ Checks setcap environment @@ -942,7 +902,6 @@ def checkSetcap() -> (bool, str, str): return False, base_executable, getcap_values return True, base_executable, getcap_values - def main(): """ Test function executed when this file is run as a discrete script @@ -964,25 +923,26 @@ def main(): print("\nERROR: Missing cap flags on python executable.\nExecutable:\t{}\nCap Values:\t{}\nSuggested command:\t{}".format(pythonPath, getCapValues, commandLine)) return - if opts.color: - tiltColor = opts.color.title() - tiltColorName = opts.color.title() + # Select color + if opts.col: + tiltColor = opts.col.title() + tiltColorName = opts.col.title() else: tiltColor = None tiltColorName = "all" - if opts.median: - median = opts.median + # Set options + if opts.med: + median = opts.med + if opts.avg: + average = opts.avg + if opts.dev: + device_id = opts.dev - if opts.average: - average = opts.average - - if opts.hci: - device_id = opts.hci - - tilt = TiltManager(tiltColor, averaging, median, device_id) + tilt = TiltManager(averaging, median, device_id) + tilt.setOpts(opts) tilt.loadSettings() - tilt.start() + tilt.start(opts.verbose) try: print("\nReporting {} Tilt values every 5 seconds. Ctrl-C to stop.".format(tiltColorName)) @@ -1000,10 +960,10 @@ def main(): hwVersion = tiltValue.hwVersion fwVersion = tiltValue.fwVersion if (hwVersion == 4): # If we are using a Pro, take advantage of it - temperature = round(tiltValue.temperature, 2) + temperature = round(tiltValue.temperature, 1) gravity = round(tiltValue.gravity, 4) else: - temperature = round(tiltValue.temperature, 2) + temperature = round(tiltValue.temperature) gravity = round(tiltValue.gravity, 3) battery = tiltValue.battery mac = tiltValue.mac @@ -1019,7 +979,6 @@ def main(): tilt.stop() return - if __name__ == "__main__": main() exit(0) diff --git a/brewpi.py b/brewpi.py index 6518359..27759e5 100755 --- a/brewpi.py +++ b/brewpi.py @@ -599,7 +599,7 @@ def initTilt(): # Set up Tilt if tilt: tilt.stop() tilt = None - tilt = Tilt.TiltManager(config['tiltColor'], 60, 10, 0) + tilt = Tilt.TiltManager(60, 10, 0) tilt.loadSettings() tilt.start() # Create prevTempJson for Tilt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..29305e4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +simplejson==3.17.2 +requests==2.21.0 +psutil==5.8.0 +configobj==5.0.6 +aioblescan==0.2.6 +GitPython==3.1.11 +pyserial==3.5 +numpy==1.16.2 diff --git a/utils/doBrewPi.sh b/utils/doBrewPi.sh index ab00896..b60c2ee 100755 --- a/utils/doBrewPi.sh +++ b/utils/doBrewPi.sh @@ -43,13 +43,15 @@ init() { ############ loop() { - local script stdOut stdErr + local script stdOut stdErr python script="$GITROOT/brewpi.py" + python="$GITROOT/venv/bin/python3" + while : do - if (python3 -u "$script" --check --donotrun); then - USE_TIMESTAMP_LOG=true python3 -u "$script" --log --datetime + if ("$python" -u "$script" --check --donotrun); then + USE_TIMESTAMP_LOG=true "$python" -u "$script" --log --datetime else sleep 1 fi diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 3ef36ca..5aba456 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -68,9 +68,9 @@ init() { . "$GITROOT/inc/nettest.inc" "$@" # Packages to be installed/checked via apt - APTPACKAGES="git python3 python3-pip python3-setuptools arduino-core apache2 php libapache2-mod-php php-cli php-cgi php-mbstring php-xml libatlas-base-dev python3-numpy python3-scipy" + APTPACKAGES="git python3 python3-pip python3-venv python3-setuptools arduino-core apache2 php libapache2-mod-php php-cli php-cgi php-mbstring php-xml libatlas-base-dev python3-numpy python3-scipy" # Packages to be installed/check via pip3 - PIP3PACKAGES="pyserial psutil simplejson configobj gitpython sentry-sdk" + PIP3PACKAGES="requirements.txt" } ############ @@ -262,50 +262,26 @@ do_packages() { # Cleanup if we updated packages if [ -n "$doCleanup" ]; then - echo -e "\nCleaning up local repositories." + echo -e "Cleaning up local repositories." apt-get clean -y||warn apt-get autoclean -y||warn apt-get autoremove --purge -y||warn else echo -e "\nNo apt updates to apply." fi - - # Install any Python packages not installed, update those installed - echo -e "\nChecking and installing required dependencies via pip3." - # shellcheck disable=SC2016 - pipcmd='pipInstalled=$(pip3 list --format=columns)' - eval "$pipcmd" - # shellcheck disable=SC2016 - pipcmd='pipInstalled=$(echo "$pipInstalled" | cut -f1 -d" ")' - eval "$pipcmd" - for pkg in ${PIP3PACKAGES,,}; do - # shellcheck disable=SC2154 - if [[ ! ${pipInstalled,,} == *"$pkg"* ]]; then - echo -e "\nInstalling '$pkg'." - pip3 install "$pkg" -q||die - else - echo -e "\nChecking for update to '$pkg'." - pip3 install "$pkg" --upgrade -q||die - fi - done } ############ ### Reset BT baud rate < Pi4 ############ -do_aioblescan() { +do_uart() { # Install aioblescan - local blerepo device fast safe file - echo -e "\nInstalling BLEacon support via aioblescan." - blerepo="https://github.com/brewpi-remix/aioblescan.git" + local device fast safe file + echo -e "\nModifying UART speeds for BLEacon support." file="/usr/bin/btuart" fast="\$HCIATTACH \/dev\/serial1 bcm43xx 921600 noflow - \$BDADDR" safe="\$HCIATTACH \/dev\/serial1 bcm43xx 460800 noflow - \$BDADDR" - rm -fr "$HOMEPATH/aioblescan" - git clone "$blerepo" "$HOMEPATH/aioblescan" - (cd "$HOMEPATH/aioblescan" || exit; python3 setup.py install) - rm -fr "$HOMEPATH/aioblescan" # Slow down uart speeds on < Pi4 if [ -f "$file" ]; then sed -i "s/$fast/$safe/g" "$file" @@ -318,24 +294,57 @@ do_aioblescan() { fi } +############ +### Set up venv +############ + +do_venv() { + # Handle venv and python libraries + local venvcmd pipcmd + echo -e "\nSetting up venv for user: brewpi." + # Copy in .bash_rc and .profile (for colors only) + cp "$HOMEPATH/.bashrc" "$GITROOT/" + cp "$HOMEPATH/.profile" "$GITROOT/" + + # Set up venv if it is not present + if [[ ! -d "/path/to/dir" ]]; then + venvcmd="python3 -m venv $GITROOT/venv --prompt bpr" + eval "$venvcmd"||die + fi + + # Activate venv + eval "deactivate 2> /dev/null" + echo "alias activate='. ./venv/bin/activate'" > "$GITROOT/.bash_aliases" + eval ". $GITROOT/venv/bin/activate"||die + + # Install any Python packages not installed, update those installed + echo -e "\nChecking and installing required dependencies via pip3." + pipcmd="pip3 install -r $GITROOT/requirements.txt" + eval "$pipcmd"||die + + # Deactivate venv + eval "deactivate"||die +} + ############ ### Main ############ main() { - init "$@" # Init and call supporting libs - const "$@" # Get script constants - asroot # Make sure we are running with root privs - help "$@" # Handle help and version requests + init "$@" # Init and call supporting libs + const "$@" # Get script constants + asroot # Make sure we are running with root privs + help "$@" # Handle help and version requests banner "starting" - apt_check # Check on apt packages - rem_php5 # Remove php5 packages - rem_nginx # Offer to remove nginx packages + apt_check # Check on apt packages + rem_php5 # Remove php5 packages + rem_nginx # Offer to remove nginx packages if [[ $KEEP_NGINX -eq 1 ]]; then keep_nginx "$@" # Attempt to reconfigure nginx fi - do_packages # Check on required packages - do_aioblescan + do_packages # Check on required packages + do_uart # Slow down UART + do_venv # Set up venv banner "complete" } From 9d10532c8ac6387faba9d67fdd7dd8a1c5d83ef2 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 06:34:24 -0600 Subject: [PATCH 05/57] Exclude aliases --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5f21024..8f9780b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Home dir files .bash_logout .bashrc +.bash_aliases .profile .bash_history .cache/ From 29a73890b6699bed426571d015e8c6ca9664a149 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 06:35:18 -0600 Subject: [PATCH 06/57] Move to Decimals, increase res --- brewpi.py | 126 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 44 deletions(-) diff --git a/brewpi.py b/brewpi.py index 6518359..ca03595 100755 --- a/brewpi.py +++ b/brewpi.py @@ -32,6 +32,7 @@ # Standard Imports import _thread +from decimal import * from distutils.version import LooseVersion import urllib.request import urllib.parse @@ -83,9 +84,6 @@ hwVersion = None compatibleHwVersion = "0.2.4" -# Change directory to where the script is -# os.chdir(os.path.dirname(sys.argv[0])) - # Settings will be read from controller, initialize with same defaults as # controller. This is mainly to show what's expected. Will all be overwritten # on the first update from the controller @@ -642,7 +640,8 @@ def renameTempKey(key): 'tb': 'TiltBatt', 'sg': 'spinSG', 'st': 'spinTemp', - 'sb': 'spinBatt'} + 'sb': 'spinBatt', + } return rename.get(key, key) @@ -737,7 +736,7 @@ def startSerial(): # Start controller else: # Wait for 10 seconds to allow an Uno to reboot logMessage("Waiting 10 seconds for board to restart.") - time.sleep(float(config.get('startupDelay', 10))) + time.sleep(int(config.get('startupDelay', 10))) logMessage("Checking software version on controller.") hwVersion = brewpiVersion.getVersionFromSerial(serialConn) @@ -800,16 +799,14 @@ def startSerial(): # Start controller logMessage("Caught a Runtime Error.") except Exception as e: - logError(e) type, value, traceback = sys.exc_info() fname = os.path.split(traceback.tb_frame.f_code.co_filename)[1] logError("Caught an unexpected exception.") logError("Error info:") - logError("\tError: ({0}): '{1}'".format( - getattr(e, 'errno', ''), getattr(e, 'strerror', ''))) logError("\tType: {0}".format(type)) logError("\tFilename: {0}".format(fname)) logError("\tLineNo: {0}".format(traceback.tb_lineno)) + logError("\tError:\n{0}".format(e)) logMessage("Caught an unexpected exception.") @@ -925,7 +922,7 @@ def loop(): # Main program loop raise socket.timeout elif messageType == "setBeer": # New constant beer temperature received try: - newTemp = float(value) + newTemp = Decimal(value) except ValueError: logMessage("Cannot convert temperature '" + value + "' to float.") @@ -947,7 +944,7 @@ def loop(): # Main program loop logMessage("advanced settings.") elif messageType == "setFridge": # New constant fridge temperature received try: - newTemp = float(value) + newTemp = Decimal(value) except ValueError: logMessage( "Cannot convert temperature '{0}' to float.".format(value)) @@ -1016,7 +1013,7 @@ def loop(): # Main program loop if 5 < newInterval < 5000: try: config = util.configSet( - 'interval', float(newInterval), configFile) + 'interval', Decimal(newInterval), configFile) except ValueError: logMessage( "Cannot convert interval '{0}' to float.".format(value)) @@ -1302,29 +1299,54 @@ def loop(): # Main program loop # Set time of last update lastTiltbridge = timestamp = time.time() + # TiltBridge report reference + # https://github.com/thorrak/tiltbridge/blob/42adac730105c0efcb4f9ef7e0cacf84f795d333/src/tilt/tiltHydrometer.cpp#L270 + # {"color", color_name()}, + # {"temp", converted_temp(false)}, + # {"tempUnit", is_celsius() ? "C" : "F"}, + # {"gravity", converted_gravity(use_raw_gravity)}, + # {"gsheets_name", gsheets_beer_name()}, + # {"weeks_on_battery", weeks_since_last_battery_change}, + # {"sends_battery", receives_battery}, + # {"high_resolution", tilt_pro}, + # {"fwVersion", version_code}, + + # {"mdns_id":"tiltbridgetft","tilts":{"Purple":{"color":"Purple","gravity":"1.0520","gsheets_name":"","temp":"70.0","tempUnit":"F","weeks_on_battery":20},"Yellow":{"color":"Yellow","gravity":"1.0329","gsheets_name":"","temp":"69.8","tempUnit":"F","weeks_on_battery":1}}} + + # TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] + if (checkKey(api['tilts'][config['tiltColor']], 'high_resolution')): + if api['tilts'][config['tiltColor']]['high_resolution']: + prevTempJson[color + 'HWVer'] = 4 + elif (checkKey(api['tilts'][config['tiltColor']], 'sends_battery')): + if api['tilts'][config['tiltColor']]['sends_battery']: + prevTempJson[color + 'HWVer'] = 5 # Battery = >=2 + else: + prevTempJson[color + 'HWVer'] = 0 + + if (checkKey(api['tilts'][config['tiltColor']], 'SWVer')): + prevTempJson[config["tiltColor"] + 'SWVer'] = int(api['tilts'][config['tiltColor']]['fwVersion']) + # Convert to proper temp unit _temp = 0 if cc['tempFormat'] == api['tilts'][config['tiltColor']]['tempUnit']: - _temp = float(api['tilts'][config['tiltColor']]['temp']) + _temp = Decimal(api['tilts'][config['tiltColor']]['temp']) elif cc['tempFormat'] == 'F': - _temp = bc.convert(float(api['tilts'][config['tiltColor']]['temp']), 'C', 'F') + _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'C', 'F') else: - _temp = bc.convert(float(api['tilts'][config['tiltColor']]['temp']), 'F', 'C') - - prevTempJson[config["tiltColor"] + 'Temp'] = _temp - prevTempJson[config["tiltColor"] + 'SG'] = float(api['tilts'][config['tiltColor']]['gravity']) + _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'F', 'C') - # high_resolution: true - # sends_battery: true - # fwVersion: 0 + _grav = Decimal(api['tilts'][config['tiltColor']]['gravity']) - # if (checkKey(api['tilts'][config['tiltColor']], 'HWVer')): - # prevTempJson[config["tiltColor"] + 'HWVer'] = int(api['tilts'][config['tiltColor']]['hwVersion']) - # if (checkKey(api['tilts'][config['tiltColor']], 'SWVer')): - # prevTempJson[config["tiltColor"] + 'SWVer'] = int(api['tilts'][config['tiltColor']]['fwVersion']) + if prevTempJson[color + 'HWVer'] == 4: + prevTempJson[color + 'SG'] = round(_grav, 4) + prevTempJson[color + 'Temp'] = round(_temp, 1) + else: + prevTempJson[color + 'SG'] = round(_grav, 3) + prevTempJson[color + 'Temp'] = round(_temp) - if (checkKey(api['tilts'][config['tiltColor']], 'weeks_on_battery')): - prevTempJson[config["tiltColor"] + 'Batt'] = int(api['tilts'][config['tiltColor']]['weeks_on_battery']) + if prevTempJson[color + 'HWVer'] >= 2: + if (checkKey(api['tilts'][config['tiltColor']], 'weeks_on_battery')): + prevTempJson[config["tiltColor"] + 'Batt'] = int(api['tilts'][config['tiltColor']]['weeks_on_battery']) # END: Tiltbridge Processing @@ -1346,6 +1368,7 @@ def loop(): # Main program loop # Get any items pending for the status box # Javascript will determine what/how to display + # We will append the proper temp suffix (unicode char includes degree sign) if cc['tempFormat'] == 'C': tempSuffix = "℃" else: @@ -1354,21 +1377,21 @@ def loop(): # Main program loop # Begin: Brew Bubbles Items if checkKey(prevTempJson, 'bbbpm'): status[statusIndex] = {} - statusType = "Airlock: " + statusType = "BB Airlock: " statusValue = str(round(prevTempJson['bbbpm'], 1)) + " bpm" status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 if checkKey(prevTempJson, 'bbamb'): if int(prevTempJson['bbamb']) > -127: status[statusIndex] = {} - statusType = "Ambient Temp: " + statusType = "BB Amb Temp: " statusValue = str(round(prevTempJson['bbamb'], 1)) + tempSuffix status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 if checkKey(prevTempJson, 'bbves'): if int(prevTempJson['bbves']) > -127: status[statusIndex] = {} - statusType = "Vessel Temp: " + statusType = "BB Ves Temp: " statusValue = str(round(prevTempJson['bbves'], 1)) + tempSuffix status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 @@ -1381,7 +1404,11 @@ def loop(): # Main program loop if prevTempJson[config['tiltColor'] + 'SG'] is not None: status[statusIndex] = {} statusType = "Tilt SG: " - statusValue = str(prevTempJson[config['tiltColor'] + 'SG']) + if checkKey(prevTempJson, config['tiltColor'] + 'HWVer') and prevTempJson[config['tiltColor'] + 'HWVer'] is not None: + if prevTempJson[config['tiltColor'] + 'HWVer'] == 4: # If we are running a Pro + statusValue = format(prevTempJson[config['tiltColor'] + 'SG'], '.4f') + else: + statusValue = format(prevTempJson[config['tiltColor'] + 'SG'], '.3f') status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 if checkKey(prevTempJson, config['tiltColor'] + 'Batt'): @@ -1400,7 +1427,11 @@ def loop(): # Main program loop if not prevTempJson[config['tiltColor'] + 'Temp'] == 0: status[statusIndex] = {} statusType = "Tilt Temp: " - statusValue = str(round(prevTempJson[config['tiltColor'] + 'Temp'], 1)) + tempSuffix + if checkKey(prevTempJson, config['tiltColor'] + 'HWVer') and prevTempJson[config['tiltColor'] + 'HWVer'] is not None: + if prevTempJson[config['tiltColor'] + 'HWVer'] == 4: # If we are running a Pro + statusValue = format(prevTempJson[config['tiltColor'] + 'Temp'], '.1f') + tempSuffix + else: + statusValue = str(round(prevTempJson[config['tiltColor'] + 'Temp'])) + tempSuffix status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 # End: Tilt Items @@ -1458,7 +1489,7 @@ def loop(): # Main program loop bgSerialConn.write('s') # If no new data has been received for serialRequestInteval seconds - if (time.time() - prevDataTime) >= float(config['interval']): + if (time.time() - prevDataTime) >= Decimal(config['interval']): if prevDataTime == 0: # First time through set the previous time prevDataTime = time.time() prevDataTime += 5 # Give the controller some time to respond to prevent requesting twice @@ -1466,7 +1497,7 @@ def loop(): # Main program loop prevDataTime += 5 # Give the controller some time to respond to prevent requesting twice # Controller not responding - elif (time.time() - prevDataTime) > float(config['interval']) + 2 * float(config['interval']): + elif (time.time() - prevDataTime) > Decimal(config['interval']) + 2 * Decimal(config['interval']): logMessage( "ERROR: Controller is not responding to new data requests.") @@ -1758,24 +1789,32 @@ def loop(): # Main program loop print() # Simply a visual hack if we are running via command line logMessage("Detected keyboard interrupt, exiting.") + + # 2020-12-26 18:31:47 [E] Caught an unexpected exception. + # 2020-12-26 18:31:47 [E] Error info: + # 2020-12-26 18:31:47 [E] Error: (): '' + # 2020-12-26 18:31:47 [E] Type: + # 2020-12-26 18:31:47 [E] Filename: brewpi.py + # 2020-12-26 18:31:47 [E] LineNo: 1321 + except Exception as e: type, value, traceback = sys.exc_info() fname = os.path.split(traceback.tb_frame.f_code.co_filename)[1] logError("Caught an unexpected exception.") logError("Error info:") - logError("\tError: ({0}): '{1}'".format( - getattr(e, 'errno', ''), getattr(e, 'strerror', ''))) logError("\tType: {0}".format(type)) logError("\tFilename: {0}".format(fname)) logError("\tLineNo: {0}".format(traceback.tb_lineno)) + logError("\tError:\n{0}".format(e)) logMessage("Caught an unexpected exception, exiting.") def shutdown(): # Process a graceful shutdown global bgSerialConn global tilt - global thread + global threads global serialConn + global bgSerialConn try: bgSerialConn # If we are running background serial, stop it @@ -1794,10 +1833,10 @@ def shutdown(): # Process a graceful shutdown tilt.stop() try: - thread # Allow any spawned threads to quit + threads # Allow any spawned threads to quit except NameError: - thread = None - if thread is not None: + threads = None + if threads is not None: for thread in threads: logMessage("Waiting for threads to finish.") _thread.join() @@ -1812,13 +1851,12 @@ def shutdown(): # Process a graceful shutdown serialConn.close() # Close port try: - conn # Close any open socket + bgSerialConn # Close any open socket except NameError: - conn = None - if conn is not None: + bgSerialConn = None + if bgSerialConn is not None: logMessage("Closing open sockets.") - phpConn.shutdown(socket.SHUT_RDWR) # Close socket - phpConn.close() + bgSerialConn.stop() # Close socket def main(): From 638bcd7bfbc55c4017f77e7757898acb3d1da056 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 06:49:38 -0600 Subject: [PATCH 07/57] Update www-data perms for group --- utils/doPerms.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/doPerms.sh b/utils/doPerms.sh index 2f12d7f..5f989f8 100755 --- a/utils/doPerms.sh +++ b/utils/doPerms.sh @@ -83,7 +83,7 @@ perms() { chown -R www-data:www-data "$wwwPath" || warn chown -R brewpi:www-data "$wwwPath/data" || warn find "$wwwPath" -type d -exec chmod 2770 {} \; || warn - find "$wwwPath" -type f -exec chmod 640 {} \; || warn + find "$wwwPath" -type f -exec chmod 660 {} \; || warn find "$wwwPath/data" -type f -exec chmod 660 {} \; || warn find "$wwwPath" -type f -name "*.json" -exec chmod 660 {} \; || warn echo -e "\nFixing file permissions for $GITROOT." From a6b24f0d9cf564db19330ea0201ea956b0f31bef Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 07:24:56 -0600 Subject: [PATCH 08/57] Get rid of weird comment error --- inc/config.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/config.inc b/inc/config.inc index 802f629..a4aa527 100644 --- a/inc/config.inc +++ b/inc/config.inc @@ -35,7 +35,7 @@ ### Usage: value="$(getVal $configItem $scriptPath)" ### Arguments: Strings representing: ### $configItem) Configuration item requested -### $scriptPath) Script root (will find config in ./settings/*) +### $scriptPath) Script root (will find config in ./settings/\*) ### Return: String value of configuration item ############ From ca280a2bc3ba8b9f2a4ea12b95109692d84d045c Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 07:38:08 -0600 Subject: [PATCH 09/57] Remove shellcheck error --- inc/config.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/inc/config.inc b/inc/config.inc index a4aa527..e7e745c 100644 --- a/inc/config.inc +++ b/inc/config.inc @@ -85,6 +85,7 @@ getVal() { val="$(readConfig "${defConfigFile}" "${configItem}")" fi val=$(printf -- "%s" "${val}";) + # shellcheck disable=SC2005 echo "$(strip "${val}")" } From a93aa801cee1dc1060b555a9d0b978ca752529e3 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 08:20:52 -0600 Subject: [PATCH 10/57] Stub in aliases --- utils/doDepends.sh | 26 +++++++- utils/doMenu.sh | 154 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 2 deletions(-) create mode 100755 utils/doMenu.sh diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 5aba456..6fb1779 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -300,7 +300,7 @@ do_uart() { do_venv() { # Handle venv and python libraries - local venvcmd pipcmd + local venvcmd pipcmd activateAlias aliasFile echo -e "\nSetting up venv for user: brewpi." # Copy in .bash_rc and .profile (for colors only) cp "$HOMEPATH/.bashrc" "$GITROOT/" @@ -314,9 +314,16 @@ do_venv() { # Activate venv eval "deactivate 2> /dev/null" - echo "alias activate='. ./venv/bin/activate'" > "$GITROOT/.bash_aliases" eval ". $GITROOT/venv/bin/activate"||die + # Set alias for activate + # shellcheck disable=SC2089 + activateAlias="alias activate='. ./venv/bin/activate'" + aliasFile="$GITROOT/.bash_aliases" + if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then + echo "$activateAlias" > "$aliasFile" + fi + # Install any Python packages not installed, update those installed echo -e "\nChecking and installing required dependencies via pip3." pipcmd="pip3 install -r $GITROOT/requirements.txt" @@ -326,6 +333,21 @@ do_venv() { eval "deactivate"||die } +############ +### Set up real user aliases +############ + +do_aliases() { + # TODO: Check for chambers and activate this + # Set alias for activate + local activateAlias aliasFile + activateAlias="alias brewpi='sudo $GITROOT/utils/doMenu.sh'" + aliasFile="$GITROOT/.bash_aliases" + if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then + echo "$activateAlias" > "$aliasFile" + fi +} + ############ ### Main ############ diff --git a/utils/doMenu.sh b/utils/doMenu.sh new file mode 100755 index 0000000..668e43f --- /dev/null +++ b/utils/doMenu.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) + +# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX). +# +# BrewPi Script RMX is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# BrewPi Script RMX is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BrewPi Script RMX. If not, see . + +# These scripts were originally a part of brewpi-script, a part of +# the BrewPi project. Legacy support (for the very popular Arduino +# controller) seems to have been discontinued in favor of new hardware. + +# Ignore unused variables: +# shellcheck disable=SC2034 +# Declare this script's constants/variables +declare SCRIPTPATH GITROOT WWWPATH TOOLPATH +# Declare /inc/const.inc file constants +declare THISSCRIPT SCRIPTNAME VERSION GITROOT GITURL GITPROJ PACKAGE +# Declare /inc/asroot.inc file constants +declare HOMEPATH REALUSER +# Declare my constants/variables + +############ +### Init +############ + +init() { + # Change to current dir (assumed to be in a repo) so we can get the git info + pushd . &> /dev/null || exit 1 + SCRIPTPATH="$( cd "$(dirname "$0")" || exit 1 ; pwd -P )" + cd "$SCRIPTPATH" || exit 1 # Move to where the script is + GITROOT="$(git rev-parse --show-toplevel)" &> /dev/null + if [ -z "$GITROOT" ]; then + echo -e "\nERROR: Unable to find my repository, did you move this file or not run as root?" + popd &> /dev/null || exit 1 + exit 1 + fi + + # Get project constants + # shellcheck source=/dev/null + . "$GITROOT/inc/const.inc" "$@" + + # Get error handling functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/error.inc" "$@" + + # Get help and version functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/asroot.inc" "$@" + + # Get help and version functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/help.inc" "$@" + + # Get config reading functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/config.inc" "$@" +} + +############ +### Create a banner +############ + +banner() { + local adj + adj="$1" + echo -e "\n***Script $THISSCRIPT $adj.***" +} + +############ +### Set up repos +############ + +getrepos() { + # Get app locations based on local config + WWWPATH="$(getVal wwwPath "$GITROOT")" + TOOLPATH="$(whatRepo "$(eval echo "/home/$(logname 2> /dev/null)/brewpi-tools-rmx/")")" + if [ ! -d "$TOOLPATH" ] || [ -z "$TOOLPATH" ]; then + TOOLPATH="$(whatRepo "/home/pi/brewpi-tools-rmx/")" + if [ ! -d "$TOOLPATH" ]; then + echo -e "\nWARN: Unable to find a local BrewPi-Tools-RMX repository." > /dev/tty + fi + fi +} + +############ +### Function: whatRepo +### Argument: String representing a directory +### Return: Location of .git within that tree, or blank if there is none +############ + +function whatRepo() { + local thisRepo thisReturn + thisRepo="$1" + if [ ! -d "$thisRepo" ]; then + return # Not a directory + elif ! ( cd "$thisRepo" && git rev-parse --git-dir &> /dev/null ); then + return # Not part of a repo + fi + pushd . &> /dev/null || exit 1 + cd "$thisRepo" || exit 1 + thisReturn=$(git rev-parse --show-toplevel) + if [ ! -d "$thisReturn" ]; then + thisReturn="" + fi + popd &> /dev/null || exit 1 + echo "$thisReturn" +} + +############ +### Add some functions +############ + + + +############ +### Main function +############ + +main() { + init "$@" # Init and call supporting libs + const "$@" + asroot # Make sure we are running with root provs + help "$@" + banner "starting" + # Enter some stuff + getrepos # Get all the paths + echo "Base name of currrent script: $THISSCRIPT" + echo "Short name of currrent script: $SCRIPTNAME" + echo "Currrent tagged version: $VERSION" + echo "Location of git root for scripts: $GITROOT" + echo "URL for origin: $GITURL" + echo "Name of Git project: $GITPROJ" + echo "Upper case name of project: $PACKAGE" + echo "Current user home path: $HOMEPATH" + echo "Real user calling script: $REALUSER" + echo "Tool Path: $TOOLPATH" + echo "Script Path: $GITROOT" + echo "WWW Path: $WWWPATH" + banner "complete" +} + +main "$@" && exit 0 From 7d49173f357ceb55fe08fd53d863402d4a3a6f2a Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 08:20:52 -0600 Subject: [PATCH 11/57] Stub in aliases --- utils/doDepends.sh | 26 +++++++- utils/doMenu.sh | 154 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 2 deletions(-) create mode 100755 utils/doMenu.sh diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 5aba456..6fb1779 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -300,7 +300,7 @@ do_uart() { do_venv() { # Handle venv and python libraries - local venvcmd pipcmd + local venvcmd pipcmd activateAlias aliasFile echo -e "\nSetting up venv for user: brewpi." # Copy in .bash_rc and .profile (for colors only) cp "$HOMEPATH/.bashrc" "$GITROOT/" @@ -314,9 +314,16 @@ do_venv() { # Activate venv eval "deactivate 2> /dev/null" - echo "alias activate='. ./venv/bin/activate'" > "$GITROOT/.bash_aliases" eval ". $GITROOT/venv/bin/activate"||die + # Set alias for activate + # shellcheck disable=SC2089 + activateAlias="alias activate='. ./venv/bin/activate'" + aliasFile="$GITROOT/.bash_aliases" + if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then + echo "$activateAlias" > "$aliasFile" + fi + # Install any Python packages not installed, update those installed echo -e "\nChecking and installing required dependencies via pip3." pipcmd="pip3 install -r $GITROOT/requirements.txt" @@ -326,6 +333,21 @@ do_venv() { eval "deactivate"||die } +############ +### Set up real user aliases +############ + +do_aliases() { + # TODO: Check for chambers and activate this + # Set alias for activate + local activateAlias aliasFile + activateAlias="alias brewpi='sudo $GITROOT/utils/doMenu.sh'" + aliasFile="$GITROOT/.bash_aliases" + if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then + echo "$activateAlias" > "$aliasFile" + fi +} + ############ ### Main ############ diff --git a/utils/doMenu.sh b/utils/doMenu.sh new file mode 100755 index 0000000..43f042d --- /dev/null +++ b/utils/doMenu.sh @@ -0,0 +1,154 @@ +#!/bin/bash + +# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) + +# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX). +# +# BrewPi Script RMX is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# BrewPi Script RMX is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BrewPi Script RMX. If not, see . + +# These scripts were originally a part of brewpi-script, a part of +# the BrewPi project. Legacy support (for the very popular Arduino +# controller) seems to have been discontinued in favor of new hardware. + +# Ignore unused variables: +# shellcheck disable=SC2034 +# Declare this script's constants/variables +declare SCRIPTPATH GITROOT WWWPATH TOOLPATH +# Declare /inc/const.inc file constants +declare THISSCRIPT SCRIPTNAME VERSION GITROOT GITURL GITPROJ PACKAGE +# Declare /inc/asroot.inc file constants +declare HOMEPATH REALUSER +# Declare my constants/variables + +############ +### Init +############ + +init() { + # Change to current dir (assumed to be in a repo) so we can get the git info + pushd . &> /dev/null || exit 1 + SCRIPTPATH="$( cd "$(dirname "$0")" || exit 1 ; pwd -P )" + cd "$SCRIPTPATH" || exit 1 # Move to where the script is + GITROOT="$(git rev-parse --show-toplevel)" &> /dev/null + if [ -z "$GITROOT" ]; then + echo -e "\nERROR: Unable to find my repository, did you move this file or not run as root?" + popd &> /dev/null || exit 1 + exit 1 + fi + + # Get project constants + # shellcheck source=/dev/null + . "$GITROOT/inc/const.inc" "$@" + + # Get error handling functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/error.inc" "$@" + + # Get help and version functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/asroot.inc" "$@" + + # Get help and version functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/help.inc" "$@" + + # Get config reading functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/config.inc" "$@" +} + +############ +### Create a banner +############ + +banner() { + local adj + adj="$1" + echo -e "\n***Script $THISSCRIPT $adj.***" +} + +############ +### Set up repos +############ + +getrepos() { + # Get app locations based on local config + WWWPATH="$(getVal wwwPath "$GITROOT")" + TOOLPATH="$(whatRepo "$(eval echo "/home/$(logname 2> /dev/null)/brewpi-tools-rmx/")")" + if [ ! -d "$TOOLPATH" ] || [ -z "$TOOLPATH" ]; then + TOOLPATH="$(whatRepo "/home/pi/brewpi-tools-rmx/")" + if [ ! -d "$TOOLPATH" ]; then + echo -e "\nWARN: Unable to find a local BrewPi-Tools-RMX repository." > /dev/tty + fi + fi +} + +############ +### Function: whatRepo +### Argument: String representing a directory +### Return: Location of .git within that tree, or blank if there is none +############ + +function whatRepo() { + local thisRepo thisReturn + thisRepo="$1" + if [ ! -d "$thisRepo" ]; then + return # Not a directory + elif ! ( cd "$thisRepo" && git rev-parse --git-dir &> /dev/null ); then + return # Not part of a repo + fi + pushd . &> /dev/null || exit 1 + cd "$thisRepo" || exit 1 + thisReturn=$(git rev-parse --show-toplevel) + if [ ! -d "$thisReturn" ]; then + thisReturn="" + fi + popd &> /dev/null || exit 1 + echo "$thisReturn" +} + +############ +### Add some functions +############ + +# TODO: Finish this file + +############ +### Main function +############ + +main() { + init "$@" # Init and call supporting libs + const "$@" + asroot # Make sure we are running with root provs + help "$@" + banner "starting" + # Enter some stuff + getrepos # Get all the paths + echo "Base name of currrent script: $THISSCRIPT" + echo "Short name of currrent script: $SCRIPTNAME" + echo "Currrent tagged version: $VERSION" + echo "Location of git root for scripts: $GITROOT" + echo "URL for origin: $GITURL" + echo "Name of Git project: $GITPROJ" + echo "Upper case name of project: $PACKAGE" + echo "Current user home path: $HOMEPATH" + echo "Real user calling script: $REALUSER" + echo "Tool Path: $TOOLPATH" + echo "Script Path: $GITROOT" + echo "WWW Path: $WWWPATH" + banner "complete" +} + +main "$@" && exit 0 From 69f3537f58790124275515aaae62ff5c31022613 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 10:49:09 -0600 Subject: [PATCH 12/57] Fix Tilt resolution and exceptions --- brewpi.py | 92 ++++++++++++++++++++++++------------------------------- 1 file changed, 40 insertions(+), 52 deletions(-) diff --git a/brewpi.py b/brewpi.py index 43bfd8c..85f4408 100755 --- a/brewpi.py +++ b/brewpi.py @@ -175,8 +175,6 @@ statusType = ['N/A', 'N/A', 'N/A', 'N/A'] statusValue = ['N/A', 'N/A', 'N/A', 'N/A'] -TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] - def getGit(): # Get the current script version @@ -601,13 +599,14 @@ def initTilt(): # Set up Tilt tilt.loadSettings() tilt.start() # Create prevTempJson for Tilt - prevTempJson.update({ - config['tiltColor'] + 'HWVer': 0, - config['tiltColor'] + 'SWVer': 0, - config['tiltColor'] + 'SG': 0, - config['tiltColor'] + 'Temp': 0, - config['tiltColor'] + 'Batt': 0 - }) + if not checkKey(prevTempJson, config['tiltColor'] + 'SG'): + prevTempJson.update({ + config['tiltColor'] + 'HWVer': 0, + config['tiltColor'] + 'SWVer': 0, + config['tiltColor'] + 'SG': 0, + config['tiltColor'] + 'Temp': 0, + config['tiltColor'] + 'Batt': 0 + }) def initISpindel(): # Initialize iSpindel @@ -1172,9 +1171,6 @@ def loop(): # Main program loop else: pass # Don't log JSON messages - # Set time of last update - lastBbApi = timestamp = time.time() - # Handle vessel temp conversion apiTemp = 0 if cc['tempFormat'] == api['temp_unit']: @@ -1207,6 +1203,9 @@ def loop(): # Main program loop 'bbamb': apiAmbient, 'bbves': apiTemp }) + + # Set time of last update + lastBbApi = timestamp = time.time() # END: Process a Brew Bubbles API POST else: @@ -1227,9 +1226,6 @@ def loop(): # Main program loop else: pass # Don't log JSON messages - # Set time of last update - lastiSpindel = timestamp = time.time() - # Convert to proper temp unit _temp = 0 if cc['tempFormat'] == api['temp_units']: @@ -1255,6 +1251,9 @@ def loop(): # Main program loop 'spinTemp': _temp }) + # Set time of last update + lastiSpindel = timestamp = time.time() + elif not ispindel: logError('iSpindel packet received but no iSpindel configuration exists in {0}settings/config.cfg'.format( util.addSlash(sys.path[0]))) @@ -1296,32 +1295,18 @@ def loop(): # Main program loop tilt.stop() tilt = None - # Set time of last update - lastTiltbridge = timestamp = time.time() - # TiltBridge report reference # https://github.com/thorrak/tiltbridge/blob/42adac730105c0efcb4f9ef7e0cacf84f795d333/src/tilt/tiltHydrometer.cpp#L270 - # {"color", color_name()}, - # {"temp", converted_temp(false)}, - # {"tempUnit", is_celsius() ? "C" : "F"}, - # {"gravity", converted_gravity(use_raw_gravity)}, - # {"gsheets_name", gsheets_beer_name()}, - # {"weeks_on_battery", weeks_since_last_battery_change}, - # {"sends_battery", receives_battery}, - # {"high_resolution", tilt_pro}, - # {"fwVersion", version_code}, - - # {"mdns_id":"tiltbridgetft","tilts":{"Purple":{"color":"Purple","gravity":"1.0520","gsheets_name":"","temp":"70.0","tempUnit":"F","weeks_on_battery":20},"Yellow":{"color":"Yellow","gravity":"1.0329","gsheets_name":"","temp":"69.8","tempUnit":"F","weeks_on_battery":1}}} - - # TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] + + # tilt.TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] if (checkKey(api['tilts'][config['tiltColor']], 'high_resolution')): if api['tilts'][config['tiltColor']]['high_resolution']: - prevTempJson[color + 'HWVer'] = 4 + prevTempJson[config['tiltColor'] + 'HWVer'] = 4 elif (checkKey(api['tilts'][config['tiltColor']], 'sends_battery')): if api['tilts'][config['tiltColor']]['sends_battery']: - prevTempJson[color + 'HWVer'] = 5 # Battery = >=2 + prevTempJson[config['tiltColor'] + 'HWVer'] = 5 # Battery = >=2 else: - prevTempJson[color + 'HWVer'] = 0 + prevTempJson[config['tiltColor'] + 'HWVer'] = 0 if (checkKey(api['tilts'][config['tiltColor']], 'SWVer')): prevTempJson[config["tiltColor"] + 'SWVer'] = int(api['tilts'][config['tiltColor']]['fwVersion']) @@ -1337,17 +1322,22 @@ def loop(): # Main program loop _grav = Decimal(api['tilts'][config['tiltColor']]['gravity']) - if prevTempJson[color + 'HWVer'] == 4: - prevTempJson[color + 'SG'] = round(_grav, 4) - prevTempJson[color + 'Temp'] = round(_temp, 1) + # Choose proper resolution for SG and Temp + if (prevTempJson[config['tiltColor'] + 'HWVer']) == 4: + prevTempJson[config['tiltColor'] + 'SG'] = round(_grav, 4) + prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp, 1) else: - prevTempJson[color + 'SG'] = round(_grav, 3) - prevTempJson[color + 'Temp'] = round(_temp) + prevTempJson[config['tiltColor'] + 'SG'] = round(_grav, 3) + prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp) - if prevTempJson[color + 'HWVer'] >= 2: + # Get battery value from anything >= Tilt v2 + if int(prevTempJson[config['tiltColor'] + 'HWVer']) >= 2: if (checkKey(api['tilts'][config['tiltColor']], 'weeks_on_battery')): prevTempJson[config["tiltColor"] + 'Batt'] = int(api['tilts'][config['tiltColor']]['weeks_on_battery']) + # Set time of last update + lastTiltbridge = timestamp = time.time() + # END: Tiltbridge Processing else: @@ -1358,8 +1348,14 @@ def loop(): # Main program loop logError(value) except Exception as e: - logError("Unknown error processing API. String received:") - logError(value) + type, value, traceback = sys.exc_info() + fname = os.path.split(traceback.tb_frame.f_code.co_filename)[1] + logError("Unknown error processing API. String received:\n{}".format(value)) + logError("Error info:") + logError("\tType: {0}".format(type)) + logError("\tFilename: {0}".format(fname)) + logError("\tLineNo: {0}".format(traceback.tb_lineno)) + logError("\tError: {0}".format(e)) elif messageType == "statusText": # Status contents requested status = {} @@ -1531,7 +1527,7 @@ def loop(): # Main program loop tiltValue = tilt.getValue(color) if tiltValue is not None: _temp = tiltValue.temperature - prevTempJson[color + 'HWVer'] = TILT_VERSIONS[tiltValue.hwVersion] + prevTempJson[color + 'HWVer'] = tiltValue.hwVersion prevTempJson[color + 'SWVer'] = tiltValue.fwVersion # Clamp temp values @@ -1789,14 +1785,6 @@ def loop(): # Main program loop print() # Simply a visual hack if we are running via command line logMessage("Detected keyboard interrupt, exiting.") - - # 2020-12-26 18:31:47 [E] Caught an unexpected exception. - # 2020-12-26 18:31:47 [E] Error info: - # 2020-12-26 18:31:47 [E] Error: (): '' - # 2020-12-26 18:31:47 [E] Type: - # 2020-12-26 18:31:47 [E] Filename: brewpi.py - # 2020-12-26 18:31:47 [E] LineNo: 1321 - except Exception as e: type, value, traceback = sys.exc_info() fname = os.path.split(traceback.tb_frame.f_code.co_filename)[1] @@ -1805,7 +1793,7 @@ def loop(): # Main program loop logError("\tType: {0}".format(type)) logError("\tFilename: {0}".format(fname)) logError("\tLineNo: {0}".format(traceback.tb_lineno)) - logError("\tError:\n{0}".format(e)) + logError("\tError: {0}".format(e)) logMessage("Caught an unexpected exception, exiting.") From 777272dd2b720c87e50da8caf73f8b4a6b7da275 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 27 Dec 2020 11:06:43 -0600 Subject: [PATCH 13/57] Fix BB temp resolution --- brewpi.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/brewpi.py b/brewpi.py index 85f4408..3c1e209 100755 --- a/brewpi.py +++ b/brewpi.py @@ -1174,22 +1174,20 @@ def loop(): # Main program loop # Handle vessel temp conversion apiTemp = 0 if cc['tempFormat'] == api['temp_unit']: - apiTemp = api['temp'] + apiTemp = Decimal(api['temp']) elif cc['tempFormat'] == 'F': - apiTemp = bc.convert(api['temp'], 'C', 'F') + apiTemp = Decimal(bc.convert(api['temp'], 'C', 'F')) else: - apiTemp = bc.convert(api['temp'], 'F', 'C') + apiTemp = Decimal(bc.convert(api['temp'], 'F', 'C')) # Handle ambient temp conversion apiAmbient = 0 if cc['tempFormat'] == api['temp_unit']: - apiAmbient = api['ambient'] + apiAmbient = Decimal(api['ambient']) elif cc['tempFormat'] == 'F': - apiAmbient = bc.convert( - api['ambient'], 'C', 'F') + apiAmbient = Decimal(bc.convert(api['ambient'], 'C', 'F')) else: - apiAmbient = bc.convert( - api['ambient'], 'F', 'C') + apiAmbient = Decimal(bc.convert(api['ambient'], 'F', 'C')) # Update prevTempJson if keys exist if checkKey(prevTempJson, 'bbbpm'): @@ -1374,21 +1372,21 @@ def loop(): # Main program loop if checkKey(prevTempJson, 'bbbpm'): status[statusIndex] = {} statusType = "BB Airlock: " - statusValue = str(round(prevTempJson['bbbpm'], 1)) + " bpm" + statusValue = format(prevTempJson['bbbpm'], '.1f') + " bpm" status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 if checkKey(prevTempJson, 'bbamb'): if int(prevTempJson['bbamb']) > -127: status[statusIndex] = {} statusType = "BB Amb Temp: " - statusValue = str(round(prevTempJson['bbamb'], 1)) + tempSuffix + statusValue = format(prevTempJson['bbamb'], '.1f') + tempSuffix status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 if checkKey(prevTempJson, 'bbves'): if int(prevTempJson['bbves']) > -127: status[statusIndex] = {} statusType = "BB Ves Temp: " - statusValue = str(round(prevTempJson['bbves'], 1)) + tempSuffix + statusValue = format(prevTempJson['bbves'], '.1f') + tempSuffix status[statusIndex].update({statusType: statusValue}) statusIndex = statusIndex + 1 # End: Brew Bubbles Items From b82fc765a8f52888571c020b46e88c79adcacd60 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 28 Dec 2020 08:58:14 -0600 Subject: [PATCH 14/57] Make dygraph SG high res, clamp vals --- brewpi.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/brewpi.py b/brewpi.py index 3c1e209..bdfbcc0 100755 --- a/brewpi.py +++ b/brewpi.py @@ -402,7 +402,7 @@ def changeWwwSetting(settingName, value): wwwSettings = {} try: - wwwSettings[settingName] = str(value) + wwwSettings[settingName] = value wwwSettingsFile.seek(0) wwwSettingsFile.write(json.dumps(wwwSettings).encode(encoding="cp437")) wwwSettingsFile.truncate() @@ -1179,6 +1179,8 @@ def loop(): # Main program loop apiTemp = Decimal(bc.convert(api['temp'], 'C', 'F')) else: apiTemp = Decimal(bc.convert(api['temp'], 'F', 'C')) + # Clamp and round temp values + apiTemp = clamp(round(apiTemp, 2), clampTempLow, clampTempHigh) # Handle ambient temp conversion apiAmbient = 0 @@ -1188,6 +1190,8 @@ def loop(): # Main program loop apiAmbient = Decimal(bc.convert(api['ambient'], 'C', 'F')) else: apiAmbient = Decimal(bc.convert(api['ambient'], 'F', 'C')) + # Clamp and round temp values + apiAmbient = clamp(round(apiAmbient, 2), clampTempLow, clampTempHigh) # Update prevTempJson if keys exist if checkKey(prevTempJson, 'bbbpm'): @@ -1234,18 +1238,23 @@ def loop(): # Main program loop else: _temp = bc.convert( api['temperature'], 'F', 'C') + # Clamp and round temp values + _temp = clamp(round(_temp, 2), clampTempLow, clampTempHigh) + + # Clamp and round gravity values + _gravity = clamp(api['gravity'], clampSGLower, clampSGUpper) # Update prevTempJson if keys exist if checkKey(prevTempJson, 'battery'): prevTempJson['spinBatt'] = api['battery'] - prevTempJson['spinSG'] = api['gravity'] + prevTempJson['spinSG'] = _gravity prevTempJson['spinTemp'] = _temp # Else, append values to prevTempJson else: prevTempJson.update({ 'spinBatt': api['battery'], - 'spinSG': api['gravity'], + 'spinSG': _gravity, 'spinTemp': _temp }) @@ -1318,14 +1327,22 @@ def loop(): # Main program loop else: _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'F', 'C') - _grav = Decimal(api['tilts'][config['tiltColor']]['gravity']) + _gravity = Decimal(api['tilts'][config['tiltColor']]['gravity']) + + # Clamp and round gravity values + _temp = clamp(_temp, clampTempLow, clampTempHigh) + + # Clamp and round temp values + _gravity = clamp(_gravity, clampSGLower, clampSGUpper) # Choose proper resolution for SG and Temp if (prevTempJson[config['tiltColor'] + 'HWVer']) == 4: - prevTempJson[config['tiltColor'] + 'SG'] = round(_grav, 4) + changeWwwSetting('isHighResTilt', True) + prevTempJson[config['tiltColor'] + 'SG'] = round(_gravity, 4) prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp, 1) else: - prevTempJson[config['tiltColor'] + 'SG'] = round(_grav, 3) + changeWwwSetting('isHighResTilt', False) + prevTempJson[config['tiltColor'] + 'SG'] = round(_gravity, 3) prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp) # Get battery value from anything >= Tilt v2 @@ -1539,9 +1556,11 @@ def loop(): # Main program loop _grav = clamp(tiltValue.gravity, clampSGLower, clampSGUpper) if prevTempJson[color + 'HWVer'] == 4: + changeWwwSetting('isHighResTilt', True) prevTempJson[color + 'SG'] = round(_grav, 4) prevTempJson[color + 'Temp'] = round(_temp, 2) else: + changeWwwSetting('isHighResTilt', False) prevTempJson[color + 'SG'] = round(_grav, 3) prevTempJson[color + 'Temp'] = round(_temp, 1) From 60aa8f7790eda819e3c66bc565259a11c532c963 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 28 Dec 2020 11:56:51 -0600 Subject: [PATCH 15/57] Add clamp values --- settings/config.cfg.example | 41 ++++++++++++++++++++++--------------- settings/defaults.cfg | 4 ++++ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/settings/config.cfg.example b/settings/config.cfg.example index 43d565c..cfe6721 100644 --- a/settings/config.cfg.example +++ b/settings/config.cfg.example @@ -1,34 +1,43 @@ # ======= settings above this line have been added automatically ======= -# uncomment settings here, these override the settings in defaults.cfg +# These are the settings which may be used in the config.cfg file -# scriptPath = /home/brewpi/ -# wwwPath = /var/www/html -# port = /dev/ttyACM0 -# altport = /dev/ttyACM1 -# boardType = arduino -# startupDelay = 1.0 -# debug = true -# tiltColor = Purple +# scriptPath = /home/brewpi/ # Path where brewpi.py may be found +# wwwPath = /var/www/html # Root of website +# port = /dev/ttyACM0 # Port to use for Arduino +# altport = /dev/ttyACM1 # Secondary port to use for Arduino +# boardType = arduino # Controller board type +# interval = 120 # Logging interval +# startupDelay = 10 # Delay to allow controller to start +# debug = true # Currently not used +# tiltColor = Purple # Color of Tilt to log +# iSpindel = Yellow # Color of iSpindel to log # Log JSON: # This controls logging to the stdout.txt log, as well as the relative # length and verbosity of the messages. -# # True = Full logging. May grow very quickly. # False = Terse messages, only a notice. This is the new default. # logJson = True -# On a standard pi installation, the defaults should work. -# If you are using a different linux flavor or Windows, you can -# set the tool locations here +# Clamp values +# Set these to the upper and lower limits you ever want to see logged +# Make sure they are appropriate for your temperature unit, they will +# not be converted. +# clampSGUpper = 1.175 +# clampSGLower = 0.970 +# clampTempHigh = 110.0 +# clampTempLow = 0.0 + +# On a standard pi installation, the defaults should work. If you are using +# a different linux flavor or Windows, you can set the tool locations here # arduinoHome = c:/arduino-1.0.4/ # avrdudeHome = # location of avr tools, defaults to arduinohome/hardware/tools # avrsizeHome = # defaults to empty string because avrsize is on path on Linux -# avrConf = # location of global avr conf. Defaults to avrdudeHome/avrdude.conf +# avrConf = # location of global avr conf. Defaults to avrdudeHome/avrdude.conf -# On Windows, the scripts defaults to an Internet socket instead of a system -# socket. +# On Windows, the scripts defaults to an Internet socket instead of a +# system socket (BEERSOCKET). (Currently not used) # useInetSocket=true # socketPort=6332 # socketHost=127.0.0.1 diff --git a/settings/defaults.cfg b/settings/defaults.cfg index 6a8cec8..bf26a3c 100644 --- a/settings/defaults.cfg +++ b/settings/defaults.cfg @@ -11,3 +11,7 @@ beerName = My BrewPi Remix Run interval = 120.0 dataLogging = active logJson = False +clampSGUpper = 1.175 +clampSGLower = 0.970 +clampTempUpper = 110.0 +clampTempLower = -5.0 From 294c0dacf497e080e57f1b5f27960224b526285d Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 28 Dec 2020 11:57:14 -0600 Subject: [PATCH 16/57] Add clamp values defaults --- BrewPiUtil.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/BrewPiUtil.py b/BrewPiUtil.py index 5b77882..39b5703 100755 --- a/BrewPiUtil.py +++ b/BrewPiUtil.py @@ -104,7 +104,11 @@ def readCfgWithDefaults(configFile = None): defCfg['beerName'] = 'My BrewPi Remix Run' defCfg['interval'] = '120.0' defCfg['dataLogging'] = 'active' - defCfg['logJson'] = True + defCfg['logJson'] = False + defCfg['clampSGUpper'] = 1.175 + defCfg['clampSGLower'] = 0.970 + defCfg['clampTempUpper'] = 110.0 + defCfg['clampTempLower'] = -5.0 defCfg.write() if configFile: From 1a2d67e8a45263746aced5e79d609b43c1ae5bcc Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 28 Dec 2020 12:01:14 -0600 Subject: [PATCH 17/57] Move clamp values to config file --- brewpi.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/brewpi.py b/brewpi.py index bdfbcc0..997e3da 100755 --- a/brewpi.py +++ b/brewpi.py @@ -141,12 +141,6 @@ lastTiltbridge = 0 timeoutTiltbridge = 300 -# Clamp values for Tilt -clampSGUpper = 1.175 -clampSGLower = 0.970 -clampTempHigh = 110.0 -clampTempLow = 25.0 - # Keep track of time between new data requests prevDataTime = 0 prevTimeOut = 0 @@ -840,11 +834,6 @@ def loop(): # Main program loop global tilt global tiltbridge global ispindel - # Clamp values for Tilt - global clampSGUpper - global clampSGLower - global clampTempHigh - global clampTempLow bc = BrewConvert.BrewConvert() run = True # Allow script loop to run @@ -1180,7 +1169,7 @@ def loop(): # Main program loop else: apiTemp = Decimal(bc.convert(api['temp'], 'F', 'C')) # Clamp and round temp values - apiTemp = clamp(round(apiTemp, 2), clampTempLow, clampTempHigh) + apiTemp = clamp(round(apiTemp, 2), Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) # Handle ambient temp conversion apiAmbient = 0 @@ -1191,7 +1180,7 @@ def loop(): # Main program loop else: apiAmbient = Decimal(bc.convert(api['ambient'], 'F', 'C')) # Clamp and round temp values - apiAmbient = clamp(round(apiAmbient, 2), clampTempLow, clampTempHigh) + apiAmbient = clamp(round(apiAmbient, 2), Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) # Update prevTempJson if keys exist if checkKey(prevTempJson, 'bbbpm'): @@ -1239,10 +1228,10 @@ def loop(): # Main program loop _temp = bc.convert( api['temperature'], 'F', 'C') # Clamp and round temp values - _temp = clamp(round(_temp, 2), clampTempLow, clampTempHigh) + _temp = clamp(round(_temp, 2), Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) # Clamp and round gravity values - _gravity = clamp(api['gravity'], clampSGLower, clampSGUpper) + _gravity = clamp(api['gravity'], Decimal(config['clampSGLower']), Decimal(config['clampSGUpper'])) # Update prevTempJson if keys exist if checkKey(prevTempJson, 'battery'): @@ -1330,10 +1319,10 @@ def loop(): # Main program loop _gravity = Decimal(api['tilts'][config['tiltColor']]['gravity']) # Clamp and round gravity values - _temp = clamp(_temp, clampTempLow, clampTempHigh) + _temp = clamp(_temp, Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) # Clamp and round temp values - _gravity = clamp(_gravity, clampSGLower, clampSGUpper) + _gravity = clamp(_gravity, Decimal(config['clampSGLower']), Decimal(config['clampSGUpper'])) # Choose proper resolution for SG and Temp if (prevTempJson[config['tiltColor'] + 'HWVer']) == 4: @@ -1546,14 +1535,14 @@ def loop(): # Main program loop prevTempJson[color + 'SWVer'] = tiltValue.fwVersion # Clamp temp values - _temp = clamp(_temp, clampTempLow, clampTempHigh) + _temp = clamp(_temp, Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) # Convert to C if cc['tempFormat'] == 'C': _temp = bc.convert(_temp, 'F', 'C') # Clamp SG Values - _grav = clamp(tiltValue.gravity, clampSGLower, clampSGUpper) + _grav = clamp(tiltValue.gravity, Decimal(config['clampSGLower']), Decimal(config['clampSGUpper'])) if prevTempJson[color + 'HWVer'] == 4: changeWwwSetting('isHighResTilt', True) From 4fe2aab60d04599e96ffae19aaef4d9b16ada607 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 28 Dec 2020 12:09:21 -0600 Subject: [PATCH 18/57] Run isort on file --- brewpi.py | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/brewpi.py b/brewpi.py index 997e3da..0c79bc7 100755 --- a/brewpi.py +++ b/brewpi.py @@ -32,43 +32,44 @@ # Standard Imports import _thread -from decimal import * -from distutils.version import LooseVersion -import urllib.request -import urllib.parse -import urllib.error -import traceback -import shutil -from pprint import pprint +import argparse +import asyncio import getopt +import grp import os +import pwd +import shutil import socket -import time -import sys import stat -import pwd -import grp -import serial +import sys +import time +import traceback +import urllib.error +import urllib.parse +import urllib.request +from decimal import * +from distutils.version import LooseVersion +from pprint import pprint +from struct import calcsize, pack, unpack + import git -import argparse +import serial import simplejson as json from configobj import ConfigObj -import socket -import asyncio -import sys -import Tilt -from struct import pack, unpack, calcsize -import temperatureProfile -import programController as programmer + +import BrewConvert import brewpiJson -from BrewPiUtil import Unbuffered, logMessage, logError, addSlash, readCfgWithDefaults +import BrewPiProcess import BrewPiUtil as util import brewpiVersion -import pinList import expandLogMessage -import BrewPiProcess +import pinList +import programController as programmer +import temperatureProfile +import Tilt from backgroundserial import BackGroundSerial -import BrewConvert +from BrewPiUtil import (Unbuffered, addSlash, logError, logMessage, + readCfgWithDefaults) # ******************************************************************** #### From a3499d5e49e7707df344b1da0808ce24b34e0907 Mon Sep 17 00:00:00 2001 From: Lee Bussy Date: Mon, 28 Dec 2020 12:32:59 -0600 Subject: [PATCH 19/57] Fix typo --- utils/doDepends.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 6fb1779..1091716 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -143,7 +143,7 @@ rem_nginx() { # Check for nginx running on port 80 nginixInstalled=$(sudo netstat -tulpn | grep :80 | grep nginx) if [ -z "$nginixInstalled" ] ; then - echo -e "\nNo nginx damon found running on port 80." + echo -e "\nNo nginx daemon found running on port 80." else echo -e "\nFound nginx packages installed. nginx will interfere with Apache2 and it is"; echo -e "recommended to uninstall. You can either do that now, or choose to reconfigure"; From 112b0ad6ba9af3b86b19e0c4aadb83d395e55b35 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 29 Dec 2020 12:33:01 -0600 Subject: [PATCH 20/57] Handle chambers with venv --- inc/userroot.inc | 34 ++++++++++++++++++++++++ utils/doDepends.sh | 66 +++++++++++++++++++++++++--------------------- 2 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 inc/userroot.inc diff --git a/inc/userroot.inc b/inc/userroot.inc new file mode 100644 index 0000000..eef9fa6 --- /dev/null +++ b/inc/userroot.inc @@ -0,0 +1,34 @@ +#!/bin/bash + +# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) + +# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX). +# +# BrewPi Script RMX is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# BrewPi Script RMX is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BrewPi Script RMX. If not, see . + +############ +### Get the BrewPi User's Home Directory +############ + +userroot() { + # Get the BrewPi user's home directory + USERROOT=$(echo "$GITROOT" | cut -d "/" -f-3) + if [[ ! -d "$USERROOT" ]]; then + echo -e "\nUnable to retrieve BrewPi's home directory. Manual install may be necessary." > /dev/tty + exit 1 + fi +} + +# Check if we were used as an include or called directly, +(return 0 2>/dev/null) || echo "Intended to be used as an include file only." diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 1091716..de8745c 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -1,5 +1,4 @@ #!/bin/bash -# shellcheck disable=SC2034 # Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) @@ -21,9 +20,9 @@ # Declare this script's constants declare SCRIPTPATH GITROOT APTPACKAGES PIP3PACKAGES REINSTALL GOODPORT GOODPORTSSL # Declare /inc/const.inc file constants -declare THISSCRIPT SCRIPTNAME VERSION GITROOT GITURL GITPROJ PACKAGE +declare THISSCRIPT GITROOT USERROOT # Declare /inc/asroot.inc file constants -declare HOMEPATH REALUSER +declare HOMEPATH # Declare placeholders for nginx work declare KEEP_NGINX @@ -46,27 +45,31 @@ init() { # Get project constants # shellcheck source=/dev/null . "$GITROOT/inc/const.inc" "$@" - + + # Get BrewPi user directory + # shellcheck source=/dev/null + . "$GITROOT/inc/userroot.inc" "$@" + # Get error handling functionality # shellcheck source=/dev/null . "$GITROOT/inc/error.inc" "$@" - + # Get help and version functionality # shellcheck source=/dev/null . "$GITROOT/inc/asroot.inc" "$@" - + # Get help and version functionality # shellcheck source=/dev/null . "$GITROOT/inc/help.inc" "$@" - + # Read configuration # shellcheck source=/dev/null . "$GITROOT/inc/config.inc" "$@" - + # Check network connectivity # shellcheck source=/dev/null . "$GITROOT/inc/nettest.inc" "$@" - + # Packages to be installed/checked via apt APTPACKAGES="git python3 python3-pip python3-venv python3-setuptools arduino-core apache2 php libapache2-mod-php php-cli php-cgi php-mbstring php-xml libatlas-base-dev python3-numpy python3-scipy" # Packages to be installed/check via pip3 @@ -299,34 +302,28 @@ do_uart() { ############ do_venv() { - # Handle venv and python libraries local venvcmd pipcmd activateAlias aliasFile - echo -e "\nSetting up venv for user: brewpi." - # Copy in .bash_rc and .profile (for colors only) - cp "$HOMEPATH/.bashrc" "$GITROOT/" - cp "$HOMEPATH/.profile" "$GITROOT/" # Set up venv if it is not present - if [[ ! -d "/path/to/dir" ]]; then - venvcmd="python3 -m venv $GITROOT/venv --prompt bpr" + if [[ ! -d "$USERROOT/venv" ]]; then + echo -e "\nSetting up venv for user: brewpi." + # Copy in .bash_rc and .profile (for colors only) + cp "$HOMEPATH/.bashrc" "$USERROOT/" + cp "$HOMEPATH/.profile" "$USERROOT/" + + venvcmd="python3 -m venv "$USERROOT/venv" --prompt bpr" eval "$venvcmd"||die + else + echo -e "\nUser venv already exists." fi # Activate venv eval "deactivate 2> /dev/null" - eval ". $GITROOT/venv/bin/activate"||die - - # Set alias for activate - # shellcheck disable=SC2089 - activateAlias="alias activate='. ./venv/bin/activate'" - aliasFile="$GITROOT/.bash_aliases" - if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then - echo "$activateAlias" > "$aliasFile" - fi + eval ". $USERROOT/venv/bin/activate"||die # Install any Python packages not installed, update those installed echo -e "\nChecking and installing required dependencies via pip3." - pipcmd="pip3 install -r $GITROOT/requirements.txt" + pipcmd="pip3 install -r $GITROOT/$PIP3PACKAGES" eval "$pipcmd"||die # Deactivate venv @@ -338,11 +335,18 @@ do_venv() { ############ do_aliases() { - # TODO: Check for chambers and activate this - # Set alias for activate - local activateAlias aliasFile - activateAlias="alias brewpi='sudo $GITROOT/utils/doMenu.sh'" + # Set alias for menu + local menuAlias activateAlias aliasFile + + menuAlias="alias brewpi='sudo $GITROOT/utils/doMenu.sh'" aliasFile="$GITROOT/.bash_aliases" + if ! grep "^$menuAlias\$" "$aliasFile" &>/dev/null; then + echo "$menuAlias" > "$aliasFile" + fi + + # Set alias for activate + activateAlias="alias activate='. ./venv/bin/activate'" + aliasFile="$USERROOT/.bash_aliases" if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then echo "$activateAlias" > "$aliasFile" fi @@ -355,6 +359,7 @@ do_aliases() { main() { init "$@" # Init and call supporting libs const "$@" # Get script constants + userroot "$@" # Get BrewPi user's home directory asroot # Make sure we are running with root privs help "$@" # Handle help and version requests banner "starting" @@ -367,6 +372,7 @@ main() { do_packages # Check on required packages do_uart # Slow down UART do_venv # Set up venv + do_aliases # Set up BrewPi user aliases banner "complete" } From 30152ab8905cfd4f757f75d714f38e5e5b235748 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 29 Dec 2020 12:56:11 -0600 Subject: [PATCH 21/57] Fix venv for chambers --- utils/doDepends.sh | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/utils/doDepends.sh b/utils/doDepends.sh index de8745c..468ae6e 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -20,7 +20,7 @@ # Declare this script's constants declare SCRIPTPATH GITROOT APTPACKAGES PIP3PACKAGES REINSTALL GOODPORT GOODPORTSSL # Declare /inc/const.inc file constants -declare THISSCRIPT GITROOT USERROOT +declare THISSCRIPT GITROOT USERROOT REALUSER # Declare /inc/asroot.inc file constants declare HOMEPATH # Declare placeholders for nginx work @@ -306,7 +306,7 @@ do_venv() { # Set up venv if it is not present if [[ ! -d "$USERROOT/venv" ]]; then - echo -e "\nSetting up venv for user: brewpi." + echo -e "\nSetting up venv for BrewPi user." # Copy in .bash_rc and .profile (for colors only) cp "$HOMEPATH/.bashrc" "$USERROOT/" cp "$HOMEPATH/.profile" "$USERROOT/" @@ -314,7 +314,7 @@ do_venv() { venvcmd="python3 -m venv "$USERROOT/venv" --prompt bpr" eval "$venvcmd"||die else - echo -e "\nUser venv already exists." + echo -e "\nBrewPi user venv already exists." fi # Activate venv @@ -338,17 +338,19 @@ do_aliases() { # Set alias for menu local menuAlias activateAlias aliasFile - menuAlias="alias brewpi='sudo $GITROOT/utils/doMenu.sh'" - aliasFile="$GITROOT/.bash_aliases" - if ! grep "^$menuAlias\$" "$aliasFile" &>/dev/null; then - echo "$menuAlias" > "$aliasFile" - fi - # Set alias for activate - activateAlias="alias activate='. ./venv/bin/activate'" + activateAlias="alias activate=" aliasFile="$USERROOT/.bash_aliases" - if ! grep "^$activateAlias\$" "$aliasFile" &>/dev/null; then - echo "$activateAlias" > "$aliasFile" + if ! grep "^$activateAlias" "$aliasFile" &>/dev/null; then + echo -e "\nAdding alias to activate venv for BrewPi user." + echo "$activateAlias'. $USERROOT/venv/bin/activate'" >> "$aliasFile" + fi + + menuAlias="alias brewpi=" + aliasFile="$HOMEPATH/.bash_aliases" + if ! grep "^$menuAlias" "$aliasFile" &>/dev/null; then + echo -e "\nAdding alias for BrewPi Menu for $REALUSER user." + echo "$menuAlias'sudo $GITROOT/utils/doMenu.sh'" >> "$aliasFile" fi } From 07c9c5ca3ff07da2224bdee8030196e9776d8d3e Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 29 Dec 2020 17:16:26 -0600 Subject: [PATCH 22/57] Fix start directory for venv --- utils/doBrewPi.sh | 22 +++++++++++++++++----- utils/doDaemon.sh | 6 +++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/utils/doBrewPi.sh b/utils/doBrewPi.sh index b60c2ee..bbb2be5 100755 --- a/utils/doBrewPi.sh +++ b/utils/doBrewPi.sh @@ -26,15 +26,28 @@ declare GITROOT init() { # Change to current dir (assumed to be in a repo) so we can get the git info pushd . &> /dev/null || exit 1 - cd "$(dirname $(readlink -e $0))" || exit 1 # Move to where the script is + SCRIPTPATH="$( cd "$(dirname "$0")" || exit 1 ; pwd -P )" + cd "$SCRIPTPATH" || exit 1 # Move to where the script is GITROOT="$(git rev-parse --show-toplevel)" &> /dev/null if [ -z "$GITROOT" ]; then - echo -e "\nERROR: Unable to find my repository, did you move this file?" + echo -e "\nERROR: Unable to find my repository, did you move this file or not run as root?" popd &> /dev/null || exit 1 exit 1 fi + # Get project constants + # shellcheck source=/dev/null + . "$GITROOT/inc/const.inc" "$@" + + # Get BrewPi user directory + USERROOT=$(echo "$GITROOT" | cut -d "/" -f-3) + + # Get error handling functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/error.inc" "$@" + # Get help and version functionality + # shellcheck source=/dev/null . "$GITROOT/inc/help.inc" "$@" } @@ -43,10 +56,9 @@ init() { ############ loop() { - local script stdOut stdErr python + local script python script="$GITROOT/brewpi.py" - python="$GITROOT/venv/bin/python3" - + python="$USERROOT/venv/bin/python3" while : do diff --git a/utils/doDaemon.sh b/utils/doDaemon.sh index 78f3694..a250784 100755 --- a/utils/doDaemon.sh +++ b/utils/doDaemon.sh @@ -47,7 +47,11 @@ init() { # Get project constants # shellcheck source=/dev/null . "$GITROOT/inc/const.inc" "$@" - + + # Get BrewPi user directory + # shellcheck source=/dev/null + . "$GITROOT/inc/userroot.inc" "$@" + # Get error handling functionality # shellcheck source=/dev/null . "$GITROOT/inc/error.inc" "$@" From c71dd53605162f21b231e1db6209a55ff9dc18f9 Mon Sep 17 00:00:00 2001 From: lbussy Date: Thu, 31 Dec 2020 14:43:11 -0600 Subject: [PATCH 23/57] Error stopping with no btctrl --- Tilt.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Tilt.py b/Tilt.py index d1da3ef..a7ea538 100755 --- a/Tilt.py +++ b/Tilt.py @@ -338,18 +338,19 @@ def stop(self): :return: None """ - self.btctrl.stop_scan_request() - command = aioblescan.HCI_Cmd_LE_Advertise(enable=False) - self.btctrl.send_command(command) - - asyncio.gather(*asyncio.Task.all_tasks()).cancel() - for thread in self.threads: - self.event_loop.call_soon_threadsafe(self.event_loop.stop) - thread.join() - - self.conn.close() - self.event_loop.close() - return + if self.btctrl: + self.btctrl.stop_scan_request() + command = aioblescan.HCI_Cmd_LE_Advertise(enable=False) + self.btctrl.send_command(command) + + asyncio.gather(*asyncio.Task.all_tasks()).cancel() + for thread in self.threads: + self.event_loop.call_soon_threadsafe(self.event_loop.stop) + thread.join() + + self.conn.close() + self.event_loop.close() + return class TiltValue: From c260c760d7897efa9f60ee105c2f4b67bcac618e Mon Sep 17 00:00:00 2001 From: lbussy Date: Thu, 14 Jan 2021 17:42:53 -0600 Subject: [PATCH 24/57] Try to capture the Tilt errors --- Tilt.py | 5 ++++- brewpi.py | 34 +++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Tilt.py b/Tilt.py index a7ea538..be50398 100755 --- a/Tilt.py +++ b/Tilt.py @@ -343,7 +343,10 @@ def stop(self): command = aioblescan.HCI_Cmd_LE_Advertise(enable=False) self.btctrl.send_command(command) - asyncio.gather(*asyncio.Task.all_tasks()).cancel() + try: + asyncio.gather(*asyncio.Task.all_tasks()).cancel() + except: + pass for thread in self.threads: self.event_loop.call_soon_threadsafe(self.event_loop.stop) thread.join() diff --git a/brewpi.py b/brewpi.py index 0c79bc7..d2569df 100755 --- a/brewpi.py +++ b/brewpi.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) @@ -587,21 +587,25 @@ def initTilt(): # Set up Tilt if not checkBluetooth(): logError("Configured for Tilt but no Bluetooth radio available.") else: - if tilt: - tilt.stop() + try: + tilt # If we are running a Tilt, stop it + except NameError: tilt = None - tilt = Tilt.TiltManager(60, 10, 0) - tilt.loadSettings() - tilt.start() - # Create prevTempJson for Tilt - if not checkKey(prevTempJson, config['tiltColor'] + 'SG'): - prevTempJson.update({ - config['tiltColor'] + 'HWVer': 0, - config['tiltColor'] + 'SWVer': 0, - config['tiltColor'] + 'SG': 0, - config['tiltColor'] + 'Temp': 0, - config['tiltColor'] + 'Batt': 0 - }) + if tilt is not None: + tilt.stop() + try: + tilt.start() + # Create prevTempJson for Tilt + if not checkKey(prevTempJson, config['tiltColor'] + 'SG'): + prevTempJson.update({ + config['tiltColor'] + 'HWVer': 0, + config['tiltColor'] + 'SWVer': 0, + config['tiltColor'] + 'SG': 0, + config['tiltColor'] + 'Temp': 0, + config['tiltColor'] + 'Batt': 0 + }) + except: + logMessage("Configured for Tilt, however no Tilt is present.") def initISpindel(): # Initialize iSpindel From 67a0b3f2fa4dfebebbe8f169a01983e96eb4437e Mon Sep 17 00:00:00 2001 From: John Date: Thu, 14 Jan 2021 19:00:47 -0500 Subject: [PATCH 25/57] Ensure HWVer is initialized for all Tilts (#164) --- brewpi.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/brewpi.py b/brewpi.py index d2569df..e7944af 100755 --- a/brewpi.py +++ b/brewpi.py @@ -591,6 +591,7 @@ def initTilt(): # Set up Tilt tilt # If we are running a Tilt, stop it except NameError: tilt = None + if tilt is not None: tilt.stop() try: @@ -607,7 +608,6 @@ def initTilt(): # Set up Tilt except: logMessage("Configured for Tilt, however no Tilt is present.") - def initISpindel(): # Initialize iSpindel global ispindel global config @@ -1300,6 +1300,7 @@ def loop(): # Main program loop # https://github.com/thorrak/tiltbridge/blob/42adac730105c0efcb4f9ef7e0cacf84f795d333/src/tilt/tiltHydrometer.cpp#L270 # tilt.TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] + if (checkKey(api['tilts'][config['tiltColor']], 'high_resolution')): if api['tilts'][config['tiltColor']]['high_resolution']: prevTempJson[config['tiltColor'] + 'HWVer'] = 4 From c127f8b3ad7adf3605a0fddd57b2657698f8618d Mon Sep 17 00:00:00 2001 From: lbussy Date: Fri, 15 Jan 2021 16:43:05 -0600 Subject: [PATCH 26/57] Fix (re)starting and stopping --- Tilt.py | 29 +++++++++++++---------------- brewpi.py | 48 ++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/Tilt.py b/Tilt.py index be50398..8b61ccf 100755 --- a/Tilt.py +++ b/Tilt.py @@ -312,7 +312,8 @@ def start(self, debug = False): # Turn debug printing on or off self.setDebug(debug) - self.event_loop = asyncio.get_event_loop() + self.event_loop = asyncio.new_event_loop() + asyncio.set_event_loop(self.event_loop) # First create and configure a raw socket self.mysocket = aioblescan.create_bt_socket(self.dev_id) @@ -338,22 +339,18 @@ def stop(self): :return: None """ - if self.btctrl: - self.btctrl.stop_scan_request() - command = aioblescan.HCI_Cmd_LE_Advertise(enable=False) - self.btctrl.send_command(command) + self.btctrl.stop_scan_request() + command = aioblescan.HCI_Cmd_LE_Advertise(enable=False) + self.btctrl.send_command(command) + asyncio.gather(*asyncio.Task.all_tasks()).cancel() - try: - asyncio.gather(*asyncio.Task.all_tasks()).cancel() - except: - pass - for thread in self.threads: - self.event_loop.call_soon_threadsafe(self.event_loop.stop) - thread.join() - - self.conn.close() - self.event_loop.close() - return + for thread in self.threads: + self.event_loop.stop() + thread.join() + + self.conn.close() + self.event_loop.close() + return class TiltValue: diff --git a/brewpi.py b/brewpi.py index e7944af..b7c936d 100755 --- a/brewpi.py +++ b/brewpi.py @@ -588,25 +588,25 @@ def initTilt(): # Set up Tilt logError("Configured for Tilt but no Bluetooth radio available.") else: try: - tilt # If we are running a Tilt, stop it - except NameError: - tilt = None - - if tilt is not None: tilt.stop() - try: - tilt.start() - # Create prevTempJson for Tilt - if not checkKey(prevTempJson, config['tiltColor'] + 'SG'): - prevTempJson.update({ - config['tiltColor'] + 'HWVer': 0, - config['tiltColor'] + 'SWVer': 0, - config['tiltColor'] + 'SG': 0, - config['tiltColor'] + 'Temp': 0, - config['tiltColor'] + 'Batt': 0 - }) except: - logMessage("Configured for Tilt, however no Tilt is present.") + pass + + tilt = None + + #try: + tilt = Tilt.TiltManager(60, 10, 0) + tilt.loadSettings() + tilt.start() + # Create prevTempJson for Tilt + if not checkKey(prevTempJson, config['tiltColor'] + 'SG'): + prevTempJson.update({ + config['tiltColor'] + 'HWVer': 0, + config['tiltColor'] + 'SWVer': 0, + config['tiltColor'] + 'SG': 0, + config['tiltColor'] + 'Temp': 0, + config['tiltColor'] + 'Batt': 0 + }) def initISpindel(): # Initialize iSpindel global ispindel @@ -1283,18 +1283,14 @@ def loop(): # Main program loop if c == config["tiltColor"]: # Found, turn off regular Tilt if tiltbridge == False: - logMessage( - "Turned on Tiltbridge.") + logMessage("Turned on Tiltbridge.") tiltbridge = True try: - tilt - except NameError: + logMessage("Stopping Tilt.") + tilt.stop() tilt = None - if tilt is not None: # If we are running a Tilt, stop it - logMessage( - "Stopping Tilt.") - tilt.stop() - tilt = None + except: + pass # TiltBridge report reference # https://github.com/thorrak/tiltbridge/blob/42adac730105c0efcb4f9ef7e0cacf84f795d333/src/tilt/tiltHydrometer.cpp#L270 From 5058dceb1ef988fbef5b88a106712cb2f6fa3b31 Mon Sep 17 00:00:00 2001 From: lbussy Date: Fri, 15 Jan 2021 18:07:17 -0600 Subject: [PATCH 27/57] Handle null TiltBridge payload --- brewpi.py | 116 +++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/brewpi.py b/brewpi.py index b7c936d..b84f1c2 100755 --- a/brewpi.py +++ b/brewpi.py @@ -1266,7 +1266,16 @@ def loop(): # Main program loop # Begin: Tiltbridge Processing elif checkKey(api, 'mdns_id') and checkKey(api, 'tilts'): - # Received JSON from Tiltbridge + # Received JSON from Tiltbridge, turn off Tilt + if tiltbridge == False: + logMessage("Turned on Tiltbridge.") + tiltbridge = True + try: + logMessage("Stopping Tilt.") + tilt.stop() + tilt = None + except: + pass # Log received line if true, false is short message, none = mute if outputJson == True: logMessage("API TB JSON Recvd: " + @@ -1279,70 +1288,63 @@ def loop(): # Main program loop # Loop through (value) and match config["tiltColor"] for t in api: if t == "tilts": - for c in api['tilts']: - if c == config["tiltColor"]: - # Found, turn off regular Tilt - if tiltbridge == False: - logMessage("Turned on Tiltbridge.") - tiltbridge = True - try: - logMessage("Stopping Tilt.") - tilt.stop() - tilt = None - except: - pass - - # TiltBridge report reference - # https://github.com/thorrak/tiltbridge/blob/42adac730105c0efcb4f9ef7e0cacf84f795d333/src/tilt/tiltHydrometer.cpp#L270 - - # tilt.TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] - - if (checkKey(api['tilts'][config['tiltColor']], 'high_resolution')): - if api['tilts'][config['tiltColor']]['high_resolution']: - prevTempJson[config['tiltColor'] + 'HWVer'] = 4 - elif (checkKey(api['tilts'][config['tiltColor']], 'sends_battery')): - if api['tilts'][config['tiltColor']]['sends_battery']: - prevTempJson[config['tiltColor'] + 'HWVer'] = 5 # Battery = >=2 - else: - prevTempJson[config['tiltColor'] + 'HWVer'] = 0 + if api['tilts']: + for c in api['tilts']: + if c == config["tiltColor"]: + # TiltBridge report reference + # https://github.com/thorrak/tiltbridge/blob/42adac730105c0efcb4f9ef7e0cacf84f795d333/src/tilt/tiltHydrometer.cpp#L270 + + # tilt.TILT_VERSIONS = ['Unknown', 'v1', 'v2', 'v3', 'Pro', 'v2 or 3'] + + if (checkKey(api['tilts'][config['tiltColor']], 'high_resolution')): + if api['tilts'][config['tiltColor']]['high_resolution']: + prevTempJson[config['tiltColor'] + 'HWVer'] = 4 + elif (checkKey(api['tilts'][config['tiltColor']], 'sends_battery')): + if api['tilts'][config['tiltColor']]['sends_battery']: + prevTempJson[config['tiltColor'] + 'HWVer'] = 5 # Battery = >=2 + else: + prevTempJson[config['tiltColor'] + 'HWVer'] = 0 - if (checkKey(api['tilts'][config['tiltColor']], 'SWVer')): - prevTempJson[config["tiltColor"] + 'SWVer'] = int(api['tilts'][config['tiltColor']]['fwVersion']) + if (checkKey(api['tilts'][config['tiltColor']], 'SWVer')): + prevTempJson[config["tiltColor"] + 'SWVer'] = int(api['tilts'][config['tiltColor']]['fwVersion']) - # Convert to proper temp unit - _temp = 0 - if cc['tempFormat'] == api['tilts'][config['tiltColor']]['tempUnit']: - _temp = Decimal(api['tilts'][config['tiltColor']]['temp']) - elif cc['tempFormat'] == 'F': - _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'C', 'F') - else: - _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'F', 'C') + # Convert to proper temp unit + _temp = 0 + if cc['tempFormat'] == api['tilts'][config['tiltColor']]['tempUnit']: + _temp = Decimal(api['tilts'][config['tiltColor']]['temp']) + elif cc['tempFormat'] == 'F': + _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'C', 'F') + else: + _temp = bc.convert(Decimal(api['tilts'][config['tiltColor']]['temp']), 'F', 'C') - _gravity = Decimal(api['tilts'][config['tiltColor']]['gravity']) + _gravity = Decimal(api['tilts'][config['tiltColor']]['gravity']) - # Clamp and round gravity values - _temp = clamp(_temp, Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) + # Clamp and round gravity values + _temp = clamp(_temp, Decimal(config['clampTempLower']), Decimal(config['clampTempUpper'])) - # Clamp and round temp values - _gravity = clamp(_gravity, Decimal(config['clampSGLower']), Decimal(config['clampSGUpper'])) + # Clamp and round temp values + _gravity = clamp(_gravity, Decimal(config['clampSGLower']), Decimal(config['clampSGUpper'])) - # Choose proper resolution for SG and Temp - if (prevTempJson[config['tiltColor'] + 'HWVer']) == 4: - changeWwwSetting('isHighResTilt', True) - prevTempJson[config['tiltColor'] + 'SG'] = round(_gravity, 4) - prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp, 1) - else: - changeWwwSetting('isHighResTilt', False) - prevTempJson[config['tiltColor'] + 'SG'] = round(_gravity, 3) - prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp) + # Choose proper resolution for SG and Temp + if (prevTempJson[config['tiltColor'] + 'HWVer']) == 4: + changeWwwSetting('isHighResTilt', True) + prevTempJson[config['tiltColor'] + 'SG'] = round(_gravity, 4) + prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp, 1) + else: + changeWwwSetting('isHighResTilt', False) + prevTempJson[config['tiltColor'] + 'SG'] = round(_gravity, 3) + prevTempJson[config['tiltColor'] + 'Temp'] = round(_temp) + + # Get battery value from anything >= Tilt v2 + if int(prevTempJson[config['tiltColor'] + 'HWVer']) >= 2: + if (checkKey(api['tilts'][config['tiltColor']], 'weeks_on_battery')): + prevTempJson[config["tiltColor"] + 'Batt'] = int(api['tilts'][config['tiltColor']]['weeks_on_battery']) - # Get battery value from anything >= Tilt v2 - if int(prevTempJson[config['tiltColor'] + 'HWVer']) >= 2: - if (checkKey(api['tilts'][config['tiltColor']], 'weeks_on_battery')): - prevTempJson[config["tiltColor"] + 'Batt'] = int(api['tilts'][config['tiltColor']]['weeks_on_battery']) + # Set time of last update + lastTiltbridge = timestamp = time.time() - # Set time of last update - lastTiltbridge = timestamp = time.time() + else: + logError("Failed to parse {} Tilt from Tiltbridge payload.".format(config["tiltColor"])) # END: Tiltbridge Processing From 640af2f50d66b850c2684d9741522c156318f415 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 24 Mar 2021 11:37:59 -0500 Subject: [PATCH 28/57] Address #168 request devices --- brewpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brewpi.py b/brewpi.py index 82a78d2..3bd8299 100755 --- a/brewpi.py +++ b/brewpi.py @@ -1114,7 +1114,7 @@ def loop(): # Main program loop pinList=pinList.getPinList(hwVersion.board, hwVersion.shield)) phpConn.send(json.dumps(response).encode('utf-8')) else: - phpConn.send("device-list-not-up-to-date") + phpConn.send(str.encode("device-list-not-up-to-date")) elif messageType == "applyDevice": # Change device settings try: # Load as JSON to check syntax From d83f05bda6d7f740ae5dd3513b484b215fe59f8c Mon Sep 17 00:00:00 2001 From: lbussy Date: Thu, 25 Mar 2021 15:49:07 -0500 Subject: [PATCH 29/57] Fix error when setting devices --- backgroundserial.py | 3 ++ brewpi.py | 86 ++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/backgroundserial.py b/backgroundserial.py index 5eaf359..961860f 100755 --- a/backgroundserial.py +++ b/backgroundserial.py @@ -84,6 +84,9 @@ def read_message(self): except queue.Empty: return None + def writeln(self, data): + self.write(data + "\n") + def write(self, data): self.exit_on_fatal_error() # prevent writing to a port in error state. This will leave unclosed handles to serial on the system diff --git a/brewpi.py b/brewpi.py index 3bd8299..74cc5cb 100755 --- a/brewpi.py +++ b/brewpi.py @@ -768,9 +768,9 @@ def startSerial(): # Start controller bgSerialConn = BackGroundSerial(serialConn) bgSerialConn.start() # Request settings from controller, processed later when reply is received - bgSerialConn.write('s') # request control settings cs - bgSerialConn.write('c') # request control constants cc - bgSerialConn.write('v') # request control variables cv + bgSerialConn.writeln("s") # request control settings cs + bgSerialConn.writeln("c") # request control constants cc + bgSerialConn.writeln("v") # request control variables cv # Answer from controller is received asynchronously later. # Keep track of time between new data requests @@ -878,17 +878,17 @@ def loop(): # Main program loop value = "" if messageType == "ack": # Acknowledge request - phpConn.send("ack".encode('utf-8')) + phpConn.send("ack".encode(encoding="utf-8")) elif messageType == "lcd": # LCD contents requested - phpConn.send(json.dumps(lcdText).encode('utf-8')) + phpConn.send(json.dumps(lcdText).encode(encoding="utf-8")) elif messageType == "getMode": # Echo mode setting - phpConn.send(cs['mode']).encode('utf-8') + phpConn.send(cs['mode'].encode(encoding="utf-8")) elif messageType == "getFridge": # Echo fridge temperature setting - phpConn.send(json.dumps(cs['fridgeSet']).encode('utf-8')) + phpConn.send(json.dumps(cs['fridgeSet']).encode(encoding="utf-8")) elif messageType == "getBeer": # Echo beer temperature setting - phpConn.send(json.dumps(cs['beerSet']).encode('utf-8')) + phpConn.send(json.dumps(cs['beerSet']).encode(encoding="utf-8")) elif messageType == "getControlConstants": # Echo control constants - phpConn.send(json.dumps(cc).encode('utf-8')) + phpConn.send(json.dumps(cc).encode(encoding="utf-8")) elif messageType == "getControlSettings": # Echo control settings if cs['mode'] == "p": profileFile = util.scriptPath() + 'settings/tempProfile.csv' @@ -896,23 +896,23 @@ def loop(): # Main program loop cs['profile'] = prof.readline().split( ",")[-1].rstrip("\n") cs['dataLogging'] = config['dataLogging'] - phpConn.send(json.dumps(cs).encode('utf-8')) + phpConn.send(json.dumps(cs).encode(encoding="utf-8")) elif messageType == "getControlVariables": # Echo control variables - phpConn.send(json.dumps(cv).encode('utf-8')) + phpConn.send(json.dumps(cv).encode(encoding="utf-8")) elif messageType == "refreshControlConstants": # Request control constants from controller - bgSerialConn.write("c") + bgSerialConn.writeln("c") raise socket.timeout elif messageType == "refreshControlSettings": # Request control settings from controller - bgSerialConn.write("s") + bgSerialConn.writeln("s") raise socket.timeout elif messageType == "refreshControlVariables": # Request control variables from controller - bgSerialConn.write("v") + bgSerialConn.writeln("v") raise socket.timeout elif messageType == "loadDefaultControlSettings": - bgSerialConn.write("S") + bgSerialConn.writeln("S") raise socket.timeout elif messageType == "loadDefaultControlConstants": - bgSerialConn.write("C") + bgSerialConn.writeln("C") raise socket.timeout elif messageType == "setBeer": # New constant beer temperature received try: @@ -925,7 +925,7 @@ def loop(): # Main program loop cs['mode'] = 'b' # Round to 2 dec, python will otherwise produce 6.999999999 cs['beerSet'] = round(newTemp, 2) - bgSerialConn.write( + bgSerialConn.writeln( "j{mode:\"b\", beerSet:" + json.dumps(cs['beerSet']) + "}") logMessage("Beer temperature set to {0} degrees by web.".format( str(cs['beerSet']))) @@ -946,7 +946,7 @@ def loop(): # Main program loop if cc['tempSetMin'] <= newTemp <= cc['tempSetMax']: cs['mode'] = 'f' cs['fridgeSet'] = round(newTemp, 2) - bgSerialConn.write("j{mode:\"f\", fridgeSet:" + + bgSerialConn.writeln("j{mode:\"f\", fridgeSet:" + json.dumps(cs['fridgeSet']) + "}") logMessage("Fridge temperature set to {0} degrees by web.".format( str(cs['fridgeSet']))) @@ -959,14 +959,14 @@ def loop(): # Main program loop logMessage("advanced settings.") elif messageType == "setOff": # Control mode set to OFF cs['mode'] = 'o' - bgSerialConn.write("j{mode:\"o\"}") + bgSerialConn.writeln("j{mode:\"o\"}") logMessage("Temperature control disabled.") raise socket.timeout elif messageType == "setParameters": # Receive JSON key:value pairs to set parameters on the controller try: decoded = json.loads(value) - bgSerialConn.write("j" + json.dumps(decoded)) + bgSerialConn.writeln("j" + json.dumps(decoded)) if 'tempFormat' in decoded: # Change in web interface settings too changeWwwSetting( @@ -1017,16 +1017,16 @@ def loop(): # Main program loop elif messageType == "startNewBrew": # New beer name newName = value result = startNewBrew(newName) - phpConn.send(json.dumps(result).encode('utf-8')) + phpConn.send(json.dumps(result).encode(encoding="utf-8")) elif messageType == "pauseLogging": # Pause logging result = pauseLogging() - phpConn.send(json.dumps(result).encode('utf-8')) + phpConn.send(json.dumps(result).encode(encoding="utf-8")) elif messageType == "stopLogging": # Stop logging result = stopLogging() - phpConn.send(json.dumps(result).encode('utf-8')) + phpConn.send(json.dumps(result).encode(encoding="utf-8")) elif messageType == "resumeLogging": # Resume logging result = resumeLogging() - phpConn.send(json.dumps(result).encode('utf-8')) + phpConn.send(json.dumps(result).encode(encoding="utf-8")) elif messageType == "dateTimeFormatDisplay": # Change date time format config = util.configSet( 'dateTimeFormatDisplay', value, configFile) @@ -1058,14 +1058,14 @@ def loop(): # Main program loop except IOError as e: # Catch all exceptions and report back an error error = "I/O Error(%d) updating profile: %s." % (e.errno, e.strerror) - phpConn.send(error) + phpConn.send(error.encode(encoding="utf-8")) logMessage(error) else: phpConn.send( - "Profile successfully updated.".encode('utf-8')) + "Profile successfully updated.".encode(encoding="utf-8")) if cs['mode'] != 'p': cs['mode'] = 'p' - bgSerialConn.write("j{mode:\"p\"}") + bgSerialConn.writeln("j{mode:\"p\"}") logMessage("Profile mode enabled.") raise socket.timeout # Go to serial communication to update controller elif messageType == "programController" or messageType == "programArduino": # Reprogram controller @@ -1099,22 +1099,22 @@ def loop(): # Main program loop deviceList['listState'] = "" # Invalidate local copy if value.find("readValues") != -1: # Request installed devices - bgSerialConn.write("d{r:1}") + bgSerialConn.writeln("d{r:1}") # Request available, but not installed devices - bgSerialConn.write("h{u:-1,v:1}") + bgSerialConn.writeln("h{u:-1,v:1}") else: - bgSerialConn.write("d{}") # Request installed devices + bgSerialConn.writeln("d{}") # Request installed devices # Request available, but not installed devices - bgSerialConn.write("h{u:-1}") + bgSerialConn.writeln("h{u:-1}") elif messageType == "getDeviceList": # Echo device list if deviceList['listState'] in ["dh", "hd"]: response = dict(board=hwVersion.board, shield=hwVersion.shield, deviceList=deviceList, pinList=pinList.getPinList(hwVersion.board, hwVersion.shield)) - phpConn.send(json.dumps(response).encode('utf-8')) + phpConn.send(json.dumps(response).encode(encoding="utf-8")) else: - phpConn.send(str.encode("device-list-not-up-to-date")) + phpConn.send("device-list-not-up-to-date".encode(encoding="utf-8")) elif messageType == "applyDevice": # Change device settings try: # Load as JSON to check syntax @@ -1123,7 +1123,7 @@ def loop(): # Main program loop logMessage( "ERROR. Invalid JSON parameter string received: {0}".format(value)) continue - bgSerialConn.write("U{0}".format( + bgSerialConn.writeln("U{0}".format( json.dumps(configStringJson))) deviceList['listState'] = "" # Invalidate local copy elif messageType == "writeDevice": # Configure a device @@ -1134,7 +1134,7 @@ def loop(): # Main program loop logMessage( "ERROR: invalid JSON parameter string received: " + value) continue - bgSerialConn.write("d" + json.dumps(configStringJson)) + bgSerialConn.writeln("d" + json.dumps(configStringJson)) elif messageType == "getVersion": # Get firmware version from controller if hwVersion: response = hwVersion.__dict__ @@ -1143,13 +1143,13 @@ def loop(): # Main program loop response['version'] = hwVersion.toString() else: response = {} - phpConn.send(json.dumps(response).encode('utf-8')) + phpConn.send(json.dumps(response).encode(encoding="utf-8")) elif messageType == "resetController": # Erase EEPROM logMessage("Resetting controller to factory defaults.") - bgSerialConn.write("E") + bgSerialConn.writeln("E") elif messageType == "api": # External API Received # Receive an API message in JSON key:value pairs - # phpConn.send("Ok") + # phpConn.send("Ok".encode(encoding="utf-8")) try: api = json.loads(value) @@ -1466,7 +1466,7 @@ def loop(): # Main program loop statusIndex = statusIndex + 1 # End: iSpindel Items - phpConn.send(json.dumps(status).encode('utf-8')) + phpConn.send(json.dumps(status).encode(encoding="utf-8")) else: # Invalid message received logMessage( "ERROR. Received invalid message on socket: " + message) @@ -1485,20 +1485,20 @@ def loop(): # Main program loop if(time.time() - prevLcdUpdate) > 5: # Request new LCD value prevLcdUpdate += 5 # Give the controller some time to respond - bgSerialConn.write('l') + bgSerialConn.writeln("l") if(time.time() - prevSettingsUpdate) > 60: # Request Settings from controller # Controller should send updates on changes, this is a periodic # update to ensure it is up to date prevSettingsUpdate += 5 # Give the controller some time to respond - bgSerialConn.write('s') + bgSerialConn.writeln("s") # If no new data has been received for serialRequestInteval seconds if (time.time() - prevDataTime) >= Decimal(config['interval']): if prevDataTime == 0: # First time through set the previous time prevDataTime = time.time() prevDataTime += 5 # Give the controller some time to respond to prevent requesting twice - bgSerialConn.write("t") # Request new from controller + bgSerialConn.writeln("t") # Request new from controller prevDataTime += 5 # Give the controller some time to respond to prevent requesting twice # Controller not responding @@ -1775,7 +1775,7 @@ def loop(): # Main program loop if newTemp != cs['beerSet']: cs['beerSet'] = newTemp # If temperature has to be updated send settings to controller - bgSerialConn.write( + bgSerialConn.writeln( "j{beerSet:" + json.dumps(cs['beerSet']) + "}") except ConnectionError as e: From aff8fdd4654782c49ba19ea209efeef5b1a789c2 Mon Sep 17 00:00:00 2001 From: lbussy Date: Sun, 28 Mar 2021 18:21:38 -0500 Subject: [PATCH 30/57] Use proper Python env --- utils/updateFirmware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/updateFirmware.py b/utils/updateFirmware.py index be66e65..e7ab12c 100755 --- a/utils/updateFirmware.py +++ b/utils/updateFirmware.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) From 74c6b0ce88e6626b318927b14e333b657e15c24b Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 12:16:07 -0500 Subject: [PATCH 31/57] New file --- utils/doFlash.sh | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100755 utils/doFlash.sh diff --git a/utils/doFlash.sh b/utils/doFlash.sh new file mode 100755 index 0000000..07f0eda --- /dev/null +++ b/utils/doFlash.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) + +# This file is part of LBussy's BrewPi Script Remix (BrewPi-Script-RMX). +# +# BrewPi Script RMX is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# BrewPi Script RMX is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BrewPi Script RMX. If not, see . + +# Declare this script's constants +declare SCRIPTPATH GITROOT +# Declare /inc/const.inc file constants +declare THISSCRIPT SCRIPTNAME VERSION GITROOT GITURL GITPROJ PACKAGE +# Declare /inc/asroot.inc file constants +declare HOMEPATH REALUSER + +############ +### Init +############ + +init() { + # Change to current dir (assumed to be in a repo) so we can get the git info + pushd . &> /dev/null || exit 1 + SCRIPTPATH="$( cd "$(dirname "$0")" || exit 1 ; pwd -P )" + cd "$SCRIPTPATH" || exit 1 # Move to where the script is + GITROOT="$(git rev-parse --show-toplevel)" &> /dev/null + if [ -z "$GITROOT" ]; then + echo -e "\nERROR: Unable to find my repository, did you move this file or not run as root?" + popd &> /dev/null || exit 1 + exit 1 + fi + + # Get project constants + # shellcheck source=/dev/null + . "$GITROOT/inc/const.inc" "$@" + + # Get error handling functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/error.inc" "$@" + + # Get help and version functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/asroot.inc" "$@" + + # Get help and version functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/help.inc" "$@" + + # Get config file read functionality + # shellcheck source=/dev/null + . "$GITROOT/inc/config.inc" "$@" +} + +############ +### Create a banner +############ + +banner() { + local adj + adj="$1" + echo -e "\n***Script $THISSCRIPT $adj.***" +} + +############ +### Flash controller +############ + +flash() { + local yn branch pythonpath + branch="${GITBRNCH,,}" + if [ ! "$branch" == "master" ]; then + branch="--beta" + else + branch="" + fi + + # TODO: Determine if we are in multi-chamber + + if [ -n "$CHAMBER" ]; then + pythonpath=$(which python) + else + pythonpath="/home/brewpi/venv/bin/python" + fi + + eval "$pythonpath -u $GITROOT/utils/updateFirmware.py $branch" +} + +############ +### Main function +############ + +main() { + init "$@" # Init and call supporting libs + const "$@" # Get script constants + asroot # Make sure we are running with root privs + help "$@" # Process help and version requests + banner "starting" + flash # Flash firmware + banner "complete" +} + +main "$@" && exit 0 From e1466b1b10265ef9ef2d4bc7cf7f5508c7d86ead Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 13:36:37 -0500 Subject: [PATCH 32/57] Bail on error --- programController.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/programController.py b/programController.py index 3762c08..e23d8f9 100755 --- a/programController.py +++ b/programController.py @@ -598,22 +598,23 @@ def flash_file(self, hexFile): bootLoaderPort = util.findSerialPort(bootLoader=True, my_port=config['port']) # bootLoaderPort = util.findSerialPort(bootLoader=True) if not bootLoaderPort: - printStdErr("\nERROR: Could not find port in bootloader.") + printStdErr("\nERROR: Could not find port in bootloader.") + return False programCommand = (avrdudehome + 'avrdude' + - ' -F' + # override device signature check - ' -e' + # erase flash and eeprom before programming. This prevents issues with corrupted EEPROM - ' -p ' + boardSettings['build.mcu'] + - ' -c ' + boardSettings['upload.protocol'] + - ' -b ' + boardSettings['upload.speed'] + - ' -P ' + bootLoaderPort + - ' -U ' + 'flash:w:' + "\"" + hexFileLocal + "\"" + - ' -C ' + avrconf) + ' -F' + # override device signature check + ' -e' + # erase flash and eeprom before programming. This prevents issues with corrupted EEPROM + ' -p ' + boardSettings['build.mcu'] + + ' -c ' + boardSettings['upload.protocol'] + + ' -b ' + boardSettings['upload.speed'] + + ' -P ' + bootLoaderPort + + ' -U ' + 'flash:w:' + "\"" + hexFileLocal + "\"" + + ' -C ' + avrconf) printStdErr("\nProgramming Arduino with avrdude.") p = sub.Popen(programCommand, stdout=sub.PIPE, - stderr=sub.PIPE, shell=True, cwd=hexFileDir) + stderr=sub.PIPE, shell=True, cwd=hexFileDir) output, errors = p.communicate() output = output.decode() errors = errors.decode() From ddf80fb472e94267156dc9540a81e1510c576a45 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 13:57:40 -0500 Subject: [PATCH 33/57] Add additional definition --- autoSerial.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/autoSerial.py b/autoSerial.py index fb19460..79742f8 100755 --- a/autoSerial.py +++ b/autoSerial.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # Copyright (C) 2018, 2019 Lee C. Bussy (@LBussy) @@ -33,10 +33,12 @@ from serial.tools import list_ports import BrewPiUtil +from pprint import pprint as pp known_devices = [ {'vid': 0x2341, 'pid': 0x0043, 'name': "Arduino Uno"}, {'vid': 0x2341, 'pid': 0x0001, 'name': "Arduino Uno"}, + {'vid': 0x2341, 'pid': 0x0243, 'name': "Arduino Uno"}, {'vid': 0x2a03, 'pid': 0x0043, 'name': "Arduino Uno"}, {'vid': 0x2a03, 'pid': 0x0001, 'name': "Arduino Uno"}, {'vid': 0x1a86, 'pid': 0x7523, 'name': "Arduino Uno"}, @@ -117,6 +119,7 @@ def configure_serial_for_device(s, d): if __name__ == '__main__': print("All ports:") + for p in find_all_serial_ports(): try: print("{0}, VID:{1:04x}, PID:{2:04x}".format(str(p), (p.vid), (p.pid))) From 213159022de78be945529a7c869456508a6b6539 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 13:58:12 -0500 Subject: [PATCH 34/57] Handle null ports --- autoSerial.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/autoSerial.py b/autoSerial.py index 79742f8..0450a8d 100755 --- a/autoSerial.py +++ b/autoSerial.py @@ -122,10 +122,13 @@ def configure_serial_for_device(s, d): for p in find_all_serial_ports(): try: - print("{0}, VID:{1:04x}, PID:{2:04x}".format(str(p), (p.vid), (p.pid))) + if (p.vid): + print("{0}, VID:{1:04x}, PID:{2:04x}".format(str(p), (p.vid), (p.pid))) + else: + print("{} has no PID.".format(str(p))) except ValueError: # could not convert pid and vid to hex - print("{0}, VID:{1}, PID:{2}".format(str(p), (p.vid), (p.pid))) + print("Value Error: {0}, VID:{1}, PID:{2}".format(str(p), (p.vid), (p.pid))) print("Compatible ports: ") for p in find_compatible_serial_ports(): print(p) From 49f017cfef073b076f77b5eb42babf2975b7b351 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 18:45:51 -0500 Subject: [PATCH 35/57] Add port conversion --- ConvertBrewPiDevice.py | 104 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100755 ConvertBrewPiDevice.py diff --git a/ConvertBrewPiDevice.py b/ConvertBrewPiDevice.py new file mode 100755 index 0000000..fe40399 --- /dev/null +++ b/ConvertBrewPiDevice.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 + +import serial +import serial.tools.list_ports +from pprint import pprint as pp # DEBUG + + +class ConvertBrewPiDevice: + """ ConvertBrewPiPorts class converts between BrewPi and system device information. """ + + def __init__(self): + self.brewpi_tty = None + self.device = None + self.serial_number = None + + def get_device_from_brewpidev(self, brewpi_tty): + self.brewpi_tty = brewpi_tty + if self.brewpi_tty and self.brewpi_tty is not None: + port_list = serial.tools.list_ports.comports(True) + for port in port_list: + if port.device == self.brewpi_tty: + hwid = port.hwid.split() + self.device = hwid[0].split("=")[1] + return self.device + + def get_device_from_serial_number(self, serial_number): + self.serial_number = serial_number + if self.serial_number and self.serial_number is not None: + port_list = serial.tools.list_ports.comports() + for port in port_list: + if port.serial_number == self.serial_number: + self.device = port.device + return self.device + + def get_serial_number_from_device(self, device): + self.device = device + if self.device and self.device is not None: + port_list = serial.tools.list_ports.comports() + for port in port_list: + if port.device == self.device: + self.serial_number = port.serial_number + return self.serial_number + + def get_serial_number_from_brewpidev(self, brewpi_tty): + self.brewpi_tty = brewpi_tty + if self.brewpi_tty and self.brewpi_tty is not None: + port_list = serial.tools.list_ports.comports(True) + for port in port_list: + if port.device == self.brewpi_tty: + hwid = port.hwid.split() + self.device = hwid[0].split("=")[1] + if self.device and self.device is not None: + port_list = serial.tools.list_ports.comports() + for port in port_list: + if port.device == self.device: + self.serial_number = port.serial_number + return self.serial_number + + def get_brewpidev_from_serial_number(self, serial_number): + self.serial_number = serial_number + if self.serial_number and self.serial_number is not None: + port_list = serial.tools.list_ports.comports() + for port in port_list: + if port.serial_number == self.serial_number: + self.device = port.device + if self.device and self.device is not None: + port_list = serial.tools.list_ports.comports(True) + try: + hwid = port.hwid.split() + if hwid[0].split("=")[1] == self.device: + self.brewpi_tty == port.device + except: + pass + return self.brewpi_tty + + def get_brewpidev_from_device(self, device): + self.device = device + if self.device and self.device is not None: + port_list = serial.tools.list_ports.comports(True) + try: + hwid = port.hwid.split() + if hwid[0].split("=")[1] == self.device: + self.brewpi_tty == port.device + except: + pass + return self.brewpi_tty + + +if __name__ == '__main__': + convert = ConvertBrewPiDevice() + print("DEBUG: get_device_from_brewpidev(/dev/ttybrewpi1): {}".format( + convert.get_device_from_brewpidev("/dev/ttybrewpi1"))) + print("DEBUG: get_device_from_serial_number(95433343933351C07232): {}".format( + convert.get_device_from_serial_number("95433343933351C07232"))) + + print("DEBUG: get_serial_number_from_device(/dev/ttyACM0): {}".format( + convert.get_serial_number_from_device("/dev/ttyACM0"))) + print("DEBUG: get_serial_number_from_brewpidev(/dev/ttybrewpi1): {}".format( + convert.get_serial_number_from_brewpidev("/dev/ttybrewpi1"))) + + print("DEBUG: get_brewpidev_from_serial_number(95433343933351C07232): {}".format( + convert.get_brewpidev_from_serial_number("95433343933351C07232"))) + print("DEBUG: get_brewpidev_from_device(/dev/ttyACM0): {}".format( + convert.get_brewpidev_from_device("/dev/ttyACM0"))) From 77eb8b6531364b95836d1c512942a0f403edd68b Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 19:19:05 -0500 Subject: [PATCH 36/57] Change examples --- ConvertBrewPiDevice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ConvertBrewPiDevice.py b/ConvertBrewPiDevice.py index fe40399..e85ab18 100755 --- a/ConvertBrewPiDevice.py +++ b/ConvertBrewPiDevice.py @@ -88,15 +88,15 @@ def get_brewpidev_from_device(self, device): if __name__ == '__main__': convert = ConvertBrewPiDevice() - print("DEBUG: get_device_from_brewpidev(/dev/ttybrewpi1): {}".format( - convert.get_device_from_brewpidev("/dev/ttybrewpi1"))) + print("DEBUG: get_device_from_brewpidev(/dev/brewpi1): {}".format( + convert.get_device_from_brewpidev("/dev/brewpi1"))) print("DEBUG: get_device_from_serial_number(95433343933351C07232): {}".format( convert.get_device_from_serial_number("95433343933351C07232"))) print("DEBUG: get_serial_number_from_device(/dev/ttyACM0): {}".format( convert.get_serial_number_from_device("/dev/ttyACM0"))) - print("DEBUG: get_serial_number_from_brewpidev(/dev/ttybrewpi1): {}".format( - convert.get_serial_number_from_brewpidev("/dev/ttybrewpi1"))) + print("DEBUG: get_serial_number_from_brewpidev(/dev/brewpi1): {}".format( + convert.get_serial_number_from_brewpidev("/dev/brewpi1"))) print("DEBUG: get_brewpidev_from_serial_number(95433343933351C07232): {}".format( convert.get_brewpidev_from_serial_number("95433343933351C07232"))) From 60b761182d2db18b8eb22a76fe7671086db47684 Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 19:19:34 -0500 Subject: [PATCH 37/57] Use real ports for flash --- programController.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/programController.py b/programController.py index e23d8f9..93d4033 100755 --- a/programController.py +++ b/programController.py @@ -46,6 +46,7 @@ import brewpiVersion import expandLogMessage from MigrateSettings import MigrateSettings +from ConvertBrewPiDevice import ConvertBrewPiDevice msg_map = {"a": "Arduino"} @@ -594,7 +595,14 @@ def flash_file(self, hexFile): hexFileLocal = os.path.basename(hexFile) time.sleep(1) + # Get serial port while in bootloader + + # Convert udev rule based port to /dev/tty* + if not config['port'].startswith("/dev/tty"): + convert = ConvertBrewPiDevice() + config['port'] = convert.get_device_from_brewpidev(config['port']) + bootLoaderPort = util.findSerialPort(bootLoader=True, my_port=config['port']) # bootLoaderPort = util.findSerialPort(bootLoader=True) if not bootLoaderPort: From f48c0dfb8d871e8726324271cba17cb621f3666b Mon Sep 17 00:00:00 2001 From: lbussy Date: Mon, 29 Mar 2021 19:20:06 -0500 Subject: [PATCH 38/57] Change choice flow --- utils/updateFirmware.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/utils/updateFirmware.py b/utils/updateFirmware.py index e7ab12c..505cff8 100755 --- a/utils/updateFirmware.py +++ b/utils/updateFirmware.py @@ -341,13 +341,14 @@ def updateFromGitHub(beta = False, doShield = False, usePinput = True, restoreSe return True if hwVersion is not None and userInput: - choice = pipeInput("\nWould you like to try to restore your settings after programming? [Y/n]: ").lower() - if not choice.startswith('y'): - restoreSettings = False - choice = pipeInput("\nWould you like me to try to restore your configured devices after" + - "\nprogramming? [Y/n]: ").lower() + choice = pipeInput("\nWould you like to try to restore your configured devices after programming?\n[Y/n]: ").lower() if not choice.startswith('y'): restoreDevices = False + restoreSettings = False + else: + choice = pipeInput("\nWould you like to try to restore your settings after programming? [Y/n]: ").lower() + if not choice.startswith('y'): + restoreSettings = False localFileName = None system1 = None From 1e1124fc36cf44c4e9f6b79c3705a883f6ded0f4 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 30 Mar 2021 07:25:29 -0500 Subject: [PATCH 39/57] Move updateFirmware to gitroot --- utils/updateFirmware.py => updateFirmware.py | 2 +- utils/doFlash.sh | 2 +- utils/doUpdate.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename utils/updateFirmware.py => updateFirmware.py (99%) diff --git a/utils/updateFirmware.py b/updateFirmware.py similarity index 99% rename from utils/updateFirmware.py rename to updateFirmware.py index 505cff8..6a5b11b 100755 --- a/utils/updateFirmware.py +++ b/updateFirmware.py @@ -35,7 +35,7 @@ import os import subprocess import psutil -sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..") # append parent directory to be able to import files +# sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..") # append parent directory to be able to import files import autoSerial from BrewPiUtil import createDontRunFile, removeDontRunFile, stopThisChamber, readCfgWithDefaults, addSlash, setupSerial, scriptPath from gitHubReleases import gitHubReleases diff --git a/utils/doFlash.sh b/utils/doFlash.sh index 07f0eda..0a972a4 100755 --- a/utils/doFlash.sh +++ b/utils/doFlash.sh @@ -92,7 +92,7 @@ flash() { pythonpath="/home/brewpi/venv/bin/python" fi - eval "$pythonpath -u $GITROOT/utils/updateFirmware.py $branch" + eval "$pythonpath -u $GITROOT/updateFirmware.py $branch" } ############ diff --git a/utils/doUpdate.sh b/utils/doUpdate.sh index 3c1be93..289d8ad 100755 --- a/utils/doUpdate.sh +++ b/utils/doUpdate.sh @@ -283,7 +283,7 @@ flash() { echo "" > /dev/tty read -rp "Do you want to flash your controller now? [y/N]: " yn < /dev/tty case "$yn" in - [Yy]* ) eval "python3 -u $SCRIPTPATH/utils/updateFirmware.py $branch" ;; + [Yy]* ) eval "python3 -u $SCRIPTPATH/updateFirmware.py $branch" ;; * ) ;; esac } From fc3cf31df7f6f962eeff185188c7250122102cb2 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 30 Mar 2021 07:25:47 -0500 Subject: [PATCH 40/57] Describe files in directory --- utils/README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/utils/README.md b/utils/README.md index 7b3c748..192e902 100644 --- a/utils/README.md +++ b/utils/README.md @@ -1,4 +1,18 @@ -Brewpi Script Remix Utilities +# ![BrewPi Remix Legacy Remix Logo](https://raw.githubusercontent.com/brewpi-remix/brewpi-www-rmx/master/images/brewpi_logo.png) -- Sooner or later this file will describe the scripts contained here. +# BrewPi Remix Remix Utilities +The `utils` directory contains `bash` scripts which are used both by the user, and the system. They are generally used to enforce conditions prior to executing a Python script, such as activating the Python virtual environment (venv) or runnign as root. + +|Filename|Description| +|---|---| +|`doBrewPi.sh`|This is the script called by the BrewPi Remix daemon which does all of the work of keeping BrewPi Remix running. You should not need to execute this script in normal operation. The daemon runs as `brewpi`, or the name of the chamber when used in multi-chamber mode.| +|`doCleanup.sh`|This is a script which is called by the upgrade process, intended to help clean up remnants of the previous version before restarting.| +|`doDaemon.sh`|This script checks and/or creates the system daemons used by BrewPi Remix: the BrewPi Remix Daemon (named for the chamber in multi-chamber mode) and the WiFIChecker.| +|`doDepends.sh`|This script checks and enforces the apt and pip dependencies for BrewPi Remix.| +|`doFlash.sh`|This script will set up and execute the `updateFirmware.py` code to flash firmware from the command line.| +|`doIndex.sh`|This script is called by the install process to generate the root web index files as symlinks.| +|`doMenu.sh`|This script is not yet used.| +|`doPerms.sh`|This script may be the most used for BrewPi Remix users. It will check all file and system permissions. After any manual manipulation of files, this script should be called in order to ensure BrewPi Remix can operate. It is often the first troubleshooting step when a user asks for help.| +|`doUpdate.sh`|This is the update script used to bring BrewPi Remix up to the current version.| +|`doWiFi.sh`|This script is the compliment to the `doBrewPi.sh` script. Historically, the Raspberry Pi has had challenges remaining connected to WiFi. This script is run by a daemon process called `wificheck` and will periodically ping the gateway. When it is unable to reach the gateway it will restart the network stack as first aid.| \ No newline at end of file From 5dd1a681e70c505aaa44191adef2e4a9b39f64ce Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 30 Mar 2021 10:57:44 -0500 Subject: [PATCH 41/57] Move to main directory --- utils/gitHubReleases.py => gitHubReleases.py | 0 utils/updater.py => updater.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename utils/gitHubReleases.py => gitHubReleases.py (100%) rename utils/updater.py => updater.py (100%) diff --git a/utils/gitHubReleases.py b/gitHubReleases.py similarity index 100% rename from utils/gitHubReleases.py rename to gitHubReleases.py diff --git a/utils/updater.py b/updater.py similarity index 100% rename from utils/updater.py rename to updater.py From e804d4f30b8d11b9990a3b662d4b17476b425558 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 30 Mar 2021 16:09:30 -0400 Subject: [PATCH 42/57] Add downloads/ to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0b424fc..2383fde 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ start.bat sftp-config.json !.gitignore .idea/ +downloads/ utils/downloads/ venv/ .gitconfig From a2809cffc29ab1d771d1332ba0c03fa3caf97ab7 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 30 Mar 2021 16:09:57 -0400 Subject: [PATCH 43/57] Translate udev port --- updateFirmware.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/updateFirmware.py b/updateFirmware.py index 6a5b11b..a6d2ce7 100755 --- a/updateFirmware.py +++ b/updateFirmware.py @@ -41,6 +41,7 @@ from gitHubReleases import gitHubReleases import brewpiVersion import programController as programmer +from ConvertBrewPiDevice import ConvertBrewPiDevice # import sentry_sdk # sentry_sdk.init("https://5644cfdc9bd24dfbaadea6bc867a8f5b@sentry.io/1803681") @@ -149,7 +150,14 @@ def updateFromGitHub(beta = False, doShield = False, usePinput = True, restoreSe printStdErr("\nUsing auto port configuration.") port, name = autoSerial.detect_port() else: - printStdErr("\nUsing port {0} according to configuration settings.".format(config['port'])) + # Convert udev rule based port to /dev/tty* + if not config['port'].startswith("/dev/tty"): + oldport = config['port'] + convert = ConvertBrewPiDevice() + config['port'] = convert.get_device_from_brewpidev(config['port']) + printStdErr("\nUsing port {0} translated from port {1}.".format(config['port'], oldport)) + else: + printStdErr("\nUsing port {0} according to configuration settings.".format(config['port'])) port, name = autoSerial.detect_port(my_port = config['port']) if not port: From 0b81f98f7a42fcad97c1ca4874ccdd00af270515 Mon Sep 17 00:00:00 2001 From: lbussy Date: Tue, 30 Mar 2021 16:19:26 -0400 Subject: [PATCH 44/57] Fix aliases --- utils/doDepends.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 468ae6e..516a4a3 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -340,7 +340,7 @@ do_aliases() { # Set alias for activate activateAlias="alias activate=" - aliasFile="$USERROOT/.bash_aliases" + aliasFile="$HOMEPATH/.bash_aliases" if ! grep "^$activateAlias" "$aliasFile" &>/dev/null; then echo -e "\nAdding alias to activate venv for BrewPi user." echo "$activateAlias'. $USERROOT/venv/bin/activate'" >> "$aliasFile" From 503280086f560c045ee1a6158d26bba8811b55f8 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 13:15:35 -0500 Subject: [PATCH 45/57] Formatting for legibility --- brewpi.py | 3 +-- utils/terminal.py => terminal.py | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename utils/terminal.py => terminal.py (100%) diff --git a/brewpi.py b/brewpi.py index 74cc5cb..81c078b 100755 --- a/brewpi.py +++ b/brewpi.py @@ -962,8 +962,7 @@ def loop(): # Main program loop bgSerialConn.writeln("j{mode:\"o\"}") logMessage("Temperature control disabled.") raise socket.timeout - elif messageType == "setParameters": - # Receive JSON key:value pairs to set parameters on the controller + elif messageType == "setParameters": # Receive JSON key:value pairs to set parameters on the controller try: decoded = json.loads(value) bgSerialConn.writeln("j" + json.dumps(decoded)) diff --git a/utils/terminal.py b/terminal.py similarity index 100% rename from utils/terminal.py rename to terminal.py From 599174107f8cc40152a03ec50011d9d375462452 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 13:22:07 -0500 Subject: [PATCH 46/57] Avoid poorly named ports --- updateFirmware.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updateFirmware.py b/updateFirmware.py index a6d2ce7..f0032dc 100755 --- a/updateFirmware.py +++ b/updateFirmware.py @@ -150,8 +150,8 @@ def updateFromGitHub(beta = False, doShield = False, usePinput = True, restoreSe printStdErr("\nUsing auto port configuration.") port, name = autoSerial.detect_port() else: - # Convert udev rule based port to /dev/tty* - if not config['port'].startswith("/dev/tty"): + # Convert udev rule based port to /dev/ttyA* + if not config['port'].startswith("/dev/ttyA"): oldport = config['port'] convert = ConvertBrewPiDevice() config['port'] = convert.get_device_from_brewpidev(config['port']) From dfde1f43f7360ef9c7f5c7b4ccab14817649cf96 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 15:00:23 -0500 Subject: [PATCH 47/57] Move from distutils to packaging --- BrewPiProcess.py | 4 ++-- MigrateSettings.py | 6 +++--- brewpiVersion.py | 10 +++++----- gitHubReleases.py | 1 - requirements.txt | 1 + tests/versionTest.py | 9 +++++++-- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/BrewPiProcess.py b/BrewPiProcess.py index e86a6a0..99adc2a 100755 --- a/BrewPiProcess.py +++ b/BrewPiProcess.py @@ -35,11 +35,11 @@ import os import sys from time import sleep -from distutils.version import LooseVersion +from packaging import version try: import psutil - if LooseVersion(psutil.__version__) < LooseVersion("2.0"): + if version.parse(psutil.__version__) < version.parse("2.0"): print("Your version of pstuil is %s \n" \ "BrewPi requires psutil 2.0 or higher, please upgrade your version of psutil.\n" \ "This can best be done via pip, please run:\n" \ diff --git a/MigrateSettings.py b/MigrateSettings.py index 26e0c53..caacabb 100755 --- a/MigrateSettings.py +++ b/MigrateSettings.py @@ -31,7 +31,7 @@ # license and credits. from collections import namedtuple, OrderedDict -from distutils.version import LooseVersion +from packaging import version import unittest # SetttingMigrate containes 3 values: @@ -102,8 +102,8 @@ def getKeyValuePairs(self, oldSettings, oldVersion, newVersion): for setting in self.restoreValidity: for oldKey in [setting.key] + setting.aliases: if oldKey in oldSettingsCopy: - if (LooseVersion(oldVersion) >= LooseVersion(setting.minVersion) and - LooseVersion(newVersion) <= LooseVersion(setting.maxVersion)): + if (version.parse(oldVersion) >= version.parse(setting.minVersion) and + version.parse(newVersion) <= version.parse(setting.maxVersion)): keyValuePairs[setting.key] = oldSettingsCopy.pop(oldKey) break return keyValuePairs, oldSettingsCopy diff --git a/brewpiVersion.py b/brewpiVersion.py index b824530..441a28b 100755 --- a/brewpiVersion.py +++ b/brewpiVersion.py @@ -34,7 +34,7 @@ import simplejson as json import sys import time -from distutils.version import LooseVersion +from packaging import version from BrewPiUtil import asciiToUnicode from serial import SerialException @@ -131,7 +131,7 @@ class AvrInfo: board_esp8266: "ESP8266"} def __init__(self, s=None): - self.version = LooseVersion("0.0.0") + self.version = version.parse("0.0.0") self.build = 0 self.commit = None self.simulator = False @@ -186,7 +186,7 @@ def parseJsonVersion(self, s): self.commit = j[AvrInfo.commit] def parseStringVersion(self, s): - self.version = LooseVersion(s) + self.version = version.parse(s) def toString(self): if self.version: @@ -217,10 +217,10 @@ def toExtendedString(self): return string def isNewer(self, versionString): - return self.version < LooseVersion(versionString) + return self.version < version.parse(versionString) def isEqual(self, versionString): - return self.version == LooseVersion(versionString) + return self.version == version.parse(versionString) def familyName(self): family = AvrInfo.families.get(self.board) diff --git a/gitHubReleases.py b/gitHubReleases.py index 7aab703..0e80f2a 100755 --- a/gitHubReleases.py +++ b/gitHubReleases.py @@ -37,7 +37,6 @@ import pwd import grp import stat -from distutils.version import LooseVersion repo = "https://api.github.com/repos/lbussy/brewpi-firmware-rmx" diff --git a/requirements.txt b/requirements.txt index 29305e4..dce3316 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ aioblescan==0.2.6 GitPython==3.1.11 pyserial==3.5 numpy==1.16.2 +packaging==20.9 diff --git a/tests/versionTest.py b/tests/versionTest.py index 18db017..4af970c 100755 --- a/tests/versionTest.py +++ b/tests/versionTest.py @@ -1,11 +1,16 @@ +#!/usr/bin/env python3 + +import sys +import os +sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/..") # append parent directory to be able to import files import unittest from brewpiVersion import AvrInfo -from distutils.version import LooseVersion +from packaging import version class VersionTestCase(unittest.TestCase): def assertVersionEqual(self, v, versionString): - self.assertEqual(v.version, LooseVersion(versionString)) + self.assertEqual(v.version, version.parse(versionString)) self.assertEqual(versionString, v.toString()) def assertEmptyVersion(self, v): From 6d090b2e14e1d25e6a4653ffb1d591e406f1316a Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 15:27:44 -0500 Subject: [PATCH 48/57] Address formatting --- updateFirmware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updateFirmware.py b/updateFirmware.py index f0032dc..65f9843 100755 --- a/updateFirmware.py +++ b/updateFirmware.py @@ -392,7 +392,7 @@ def updateFromGitHub(beta = False, doShield = False, usePinput = True, restoreSe # Only restart if it was running when we started removeDontRunFile('{0}do_not_run_brewpi'.format(addSlash(config['wwwPath']))) else: - printStdErr('\nBrewPi was not running when we started, leaving do_not_run_brewpi in\n{0}.'.format(addSlash(config['wwwPath']))) + printStdErr('\nBrewPi was not running when we started, leaving\ndo_not_run_brewpi in\n{0}.'.format(addSlash(config['wwwPath']))) return result From 2366deff7b20fcb875a4b6864ecaa397f3f82717 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 15:32:58 -0500 Subject: [PATCH 49/57] Revert choice to restore settings --- updateFirmware.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/updateFirmware.py b/updateFirmware.py index 65f9843..5794aea 100755 --- a/updateFirmware.py +++ b/updateFirmware.py @@ -349,14 +349,13 @@ def updateFromGitHub(beta = False, doShield = False, usePinput = True, restoreSe return True if hwVersion is not None and userInput: + choice = pipeInput("\nWould you like to try to restore your settings after programming? [Y/n]: ").lower() + if not choice.startswith('y'): + restoreSettings = False + choice = pipeInput("\nWould you like to try to restore your configured devices after programming?\n[Y/n]: ").lower() if not choice.startswith('y'): restoreDevices = False - restoreSettings = False - else: - choice = pipeInput("\nWould you like to try to restore your settings after programming? [Y/n]: ").lower() - if not choice.startswith('y'): - restoreSettings = False localFileName = None system1 = None From 440e675eed80088386f35a91a03010f15b6b2031 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 16:00:29 -0500 Subject: [PATCH 50/57] Make sure asroot passes all args --- inc/asroot.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inc/asroot.inc b/inc/asroot.inc index 2e1b4a4..514e66a 100644 --- a/inc/asroot.inc +++ b/inc/asroot.inc @@ -43,13 +43,13 @@ asroot() { retval="$?" if [[ "$retval" == "0" ]]; then # If we are not root/sudo, relaunch as such - echo -e "\nNot running as root, relaunching correctly." > /dev/tty + echo -e "\nNot running as root, relaunching." > /dev/tty sleep 2 - eval "sudo bash $SCRIPTPATH/$THISSCRIPT $*" + eval "sudo bash $SCRIPTPATH/$THISSCRIPT $@" exit $? else # sudo not available, give instructions - echo -e "\nThis script must be run as root: sudo $SCRIPTPATH/$THISSCRIPT $*" 1>&2 > /dev/tty + echo -e "\nThis script must be run as root: sudo $SCRIPTPATH/$THISSCRIPT $@" 1>&2 > /dev/tty exit 1 fi fi From 55b0096bf3d6d6b3ae96ce14473e4233050e9e0d Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 17:01:25 -0500 Subject: [PATCH 51/57] Update copyright --- inc/help.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/help.inc b/inc/help.inc index 4194b97..c27a687 100644 --- a/inc/help.inc +++ b/inc/help.inc @@ -43,7 +43,7 @@ usage() { version() { cat << EOF -"$SCRIPTNAME" Copyright (C) 2019 Lee C. Bussy (@LBussy) +"$SCRIPTNAME" Copyright (C) 20219 Lee C. Bussy (@LBussy) This program comes with ABSOLUTELY NO WARRANTY. From 8db8cd8cc2a0166719ef2c3e92f2819bd430fdf0 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 17:02:54 -0500 Subject: [PATCH 52/57] Do not allow restore from newer --- programController.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/programController.py b/programController.py index 93d4033..f6bdfee 100755 --- a/programController.py +++ b/programController.py @@ -45,6 +45,7 @@ import BrewPiUtil as util import brewpiVersion import expandLogMessage +from packaging import version from MigrateSettings import MigrateSettings from ConvertBrewPiDevice import ConvertBrewPiDevice @@ -292,10 +293,12 @@ def parse_restore_settings(self, restoreWhat): restoreDevices = False if 'settings' in restoreWhat: if restoreWhat['settings']: - restoreSettings = True + if version.parse(self.versionNew) >= version.parse(self.versionOld): # Only restore settings on same or newer + restoreSettings = True if 'devices' in restoreWhat: if restoreWhat['devices']: - restoreDevices = True + if version.parse(self.versionNew) >= version.parse(self.versionOld): # Only restore devices on same or newer + restoreDevices = True # Even when restoreSettings and restoreDevices are set to True here, # they might be set to false due to version incompatibility later From 011e010bc3d85d15b08d04b619fff3cde233ab3b Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 17:03:27 -0500 Subject: [PATCH 53/57] Pass through proper args --- utils/doFlash.sh | 98 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/utils/doFlash.sh b/utils/doFlash.sh index 0a972a4..6ad4f14 100755 --- a/utils/doFlash.sh +++ b/utils/doFlash.sh @@ -18,7 +18,7 @@ # along with BrewPi Script RMX. If not, see . # Declare this script's constants -declare SCRIPTPATH GITROOT +declare SCRIPTPATH GITROOT ARGUMENTS # Declare /inc/const.inc file constants declare THISSCRIPT SCRIPTNAME VERSION GITROOT GITURL GITPROJ PACKAGE # Declare /inc/asroot.inc file constants @@ -52,15 +52,74 @@ init() { # shellcheck source=/dev/null . "$GITROOT/inc/asroot.inc" "$@" - # Get help and version functionality - # shellcheck source=/dev/null - . "$GITROOT/inc/help.inc" "$@" - # Get config file read functionality # shellcheck source=/dev/null . "$GITROOT/inc/config.inc" "$@" } + +############ +### Process this file's help +############ + +# Outputs to stdout the --help usage message. +usage() { +cat << EOF + +"$SCRIPTNAME" usage: $SCRIPTPATH/$THISSCRIPT + +Available options: + --silent or -s: Use default options, do not ask for user input + --beta or -b: Include unstable (prerelease) releases + --shield or -d: Allow flashing a different shield +EOF +} + +# Outputs to stdout the --version message. +version() { +cat << EOF + +"$SCRIPTNAME" Copyright (C) 2021 Lee C. Bussy (@LBussy) + +This program comes with ABSOLUTELY NO WARRANTY. + +This is free software, and you are welcome to redistribute it +under certain conditions. + +There is NO WARRANTY, to the extent permitted by law. +EOF +} + +localhelp() { + local arg + for arg in "$@" + do + arg="${arg//-}" # Strip out all dashes + if [[ "$arg" == "h"* ]]; then usage; exit 0 + elif [[ "$arg" == "v"* ]]; then version; exit 0 + elif [[ "$arg" == "shield"* ]] || [[ "$arg" == "d"* ]]; then shield + elif [[ "$arg" == "s"* ]]; then silent + elif [[ "$arg" == "b"* ]]; then beta + fi + done + +} + +silent() { + # --silent or -s: Use default options, do not ask for user input + ARGUMENTS="$ARGUMENTS --silent" +} + +beta() { + # --beta or -b: Include unstable (prerelease) releases + ARGUMENTS="$ARGUMENTS --beta" +} + +shield() { + # --shield or -d: Allow flashing a different shield + ARGUMENTS="$ARGUMENTS --shield" +} + ############ ### Create a banner ############ @@ -77,22 +136,15 @@ banner() { flash() { local yn branch pythonpath + # Check to see if we should allow beta code automatically branch="${GITBRNCH,,}" - if [ ! "$branch" == "master" ]; then - branch="--beta" - else - branch="" - fi - - # TODO: Determine if we are in multi-chamber - - if [ -n "$CHAMBER" ]; then - pythonpath=$(which python) - else - pythonpath="/home/brewpi/venv/bin/python" + if [ ! "$branch" == "master" ] && [[ ! "$ARGUMENTS" == *"beta"* ]]; then + ARGUMENTS="$ARGUMENTS --beta" fi - eval "$pythonpath -u $GITROOT/updateFirmware.py $branch" + # Not a glamourous way to run in the venv but it's effective + pythonpath="/home/brewpi/venv/bin/python" + eval "$pythonpath -u $GITROOT/updateFirmware.py $ARGUMENTS" } ############ @@ -100,12 +152,12 @@ flash() { ############ main() { - init "$@" # Init and call supporting libs - const "$@" # Get script constants - asroot # Make sure we are running with root privs - help "$@" # Process help and version requests + init "$@" # Init and call supporting libs + const "$@" # Get script constants + localhelp "$@" # Process local help + asroot "$@" # Make sure we are running with root privs banner "starting" - flash # Flash firmware + flash "$@" # Flash firmware banner "complete" } From f57a866d1b1b09333771d090356daea55e452792 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 17:04:53 -0500 Subject: [PATCH 54/57] Update copyright --- inc/help.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/help.inc b/inc/help.inc index c27a687..b253733 100644 --- a/inc/help.inc +++ b/inc/help.inc @@ -43,7 +43,7 @@ usage() { version() { cat << EOF -"$SCRIPTNAME" Copyright (C) 20219 Lee C. Bussy (@LBussy) +"$SCRIPTNAME" Copyright (C) 2021 Lee C. Bussy (@LBussy) This program comes with ABSOLUTELY NO WARRANTY. From 7e4168eecc2c9de28a07489290fa23cbeae23003 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 17:18:35 -0500 Subject: [PATCH 55/57] Fix aliases for both users --- utils/doDepends.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 516a4a3..e76ce40 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -341,7 +341,14 @@ do_aliases() { # Set alias for activate activateAlias="alias activate=" aliasFile="$HOMEPATH/.bash_aliases" + aliasBrewPiFile="$USERROOT/.bash_aliases" + # Check/add alias to current user for BrewPi's venv if ! grep "^$activateAlias" "$aliasFile" &>/dev/null; then + echo -e "\nAdding alias to activate venv for $REALUSER user." + echo "$activateAlias'. $USERROOT/venv/bin/activate'" >> "$aliasFile" + fi + # Check/add alias to BrewPi user for BrewPi's venv + if ! grep "^$activateAlias" "$aliasBrewPiFile" &>/dev/null; then echo -e "\nAdding alias to activate venv for BrewPi user." echo "$activateAlias'. $USERROOT/venv/bin/activate'" >> "$aliasFile" fi From c4d4fb9f922e14d4a679f374a943bc0c1f02d4f0 Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 17:44:37 -0500 Subject: [PATCH 56/57] Hard code brewpi home path --- utils/doDepends.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/doDepends.sh b/utils/doDepends.sh index e76ce40..38c2338 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -353,6 +353,8 @@ do_aliases() { echo "$activateAlias'. $USERROOT/venv/bin/activate'" >> "$aliasFile" fi + echo -e "$HOMEPATH" + read -rp "DEBUG Pause: " yn < /dev/tty menuAlias="alias brewpi=" aliasFile="$HOMEPATH/.bash_aliases" if ! grep "^$menuAlias" "$aliasFile" &>/dev/null; then From 544442f24ae7472610bfcc17c8b4780dc2c65ccb Mon Sep 17 00:00:00 2001 From: lbussy Date: Wed, 31 Mar 2021 18:10:34 -0500 Subject: [PATCH 57/57] Hard code brewpi home path --- utils/doDepends.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/utils/doDepends.sh b/utils/doDepends.sh index 38c2338..72f7e6d 100755 --- a/utils/doDepends.sh +++ b/utils/doDepends.sh @@ -353,10 +353,7 @@ do_aliases() { echo "$activateAlias'. $USERROOT/venv/bin/activate'" >> "$aliasFile" fi - echo -e "$HOMEPATH" - read -rp "DEBUG Pause: " yn < /dev/tty - menuAlias="alias brewpi=" - aliasFile="$HOMEPATH/.bash_aliases" + aliasFile="/home/brewpi/.bash_aliases" if ! grep "^$menuAlias" "$aliasFile" &>/dev/null; then echo -e "\nAdding alias for BrewPi Menu for $REALUSER user." echo "$menuAlias'sudo $GITROOT/utils/doMenu.sh'" >> "$aliasFile"