From f1564e1585f08b70ade4293082cef2e1e789e2fc Mon Sep 17 00:00:00 2001 From: Chris Date: Fri, 14 Jun 2024 08:41:15 -0400 Subject: [PATCH 1/7] Base: Move last will setup to part of initialization. MiniMQTT specifically gets cranky if you try to set the last will while connected and it's simpler to set it up front than in the connect method. Base: Add a total connect failure counter and raise exception if exceeded. This will percolate through and cause a board reset when hit. MiniMQTT has some issues where it doesn't properly release the socket so no amount of retries works. CircuitPython: Catch OSError and BrokenPipeError exceptions from the publish method. This will occur when the broker goes away during publish. We want to catch this and try to reconnect instead of failing entirely. Circuitpy-code: Report exception type when an unhandled exception comes through. This helps debugging. --- brickmaster2/core.py | 1 - brickmaster2/network/base.py | 20 ++++++++++++-------- brickmaster2/network/circuitpython.py | 19 +++++++++++++++---- circuitpy-code.py | 1 + 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/brickmaster2/core.py b/brickmaster2/core.py index f44eeb8..c789d6c 100644 --- a/brickmaster2/core.py +++ b/brickmaster2/core.py @@ -145,7 +145,6 @@ def run(self): self._logger.debug("Core: Entering run loop.") while True: # Poll the network. - self._logger.debug("Core: Polling network.") self._network.poll() # If there's an active script, do it. diff --git a/brickmaster2/network/base.py b/brickmaster2/network/base.py index b8f0b5f..ec8b1a1 100644 --- a/brickmaster2/network/base.py +++ b/brickmaster2/network/base.py @@ -92,6 +92,7 @@ def __init__(self, core, system_id, short_name, long_name, broker, mqtt_username self._reconnect_timestamp = None self._logger.debug("Network: Setting internal MQTT tracker False at startup.") self._mqtt_connected = False + self._total_failures = 0 # List for commands received and to be passed upward. self._upward_commands = [] @@ -106,6 +107,11 @@ def __init__(self, core, system_id, short_name, long_name, broker, mqtt_username self._logger.info("Defined Client ID: {}".format(self._system_id)) self._setup_mqtt() # Create the MQTT Object, connect basic callbacks + # Setup the will. + # Set the last will prior to connecting. + self._logger.info("Creating last will.") + self._mc_will_set(topic="brickmaster2/" + self._short_name + "/connectivity", + payload='offline', qos=0, retain=True) self._logger.info('Network: Initialization complete.') @@ -156,7 +162,6 @@ def poll(self): } # If interface is up but broker is not connected, retry every 30s. - self._logger.debug(f"Network: MQTT connection status from BM2 Tracker is '{self._mqtt_connected}'") if not self._mqtt_connected: try_reconnect = False # Has is been 30s since the previous attempt? @@ -165,8 +170,6 @@ def poll(self): self._logger.info("Network: 30s since previous MQTT connection attempt. Retrying...") try_reconnect = True self._reconnect_timestamp = time.monotonic() - else: - self._logger.debug("Network: Too soon to retry MQTT connection") except TypeError: try_reconnect = True self._reconnect_timestamp = time.monotonic() @@ -247,16 +250,17 @@ def _connect_mqtt(self): :return: """ - # Set the last will prior to connecting. - self._logger.info("Creating last will.") - self._mc_will_set(topic="brickmaster2/" + self._short_name + "/connectivity", - payload='offline', qos=0, retain=True) + self._logger.debug("Network: Attempting MQTT connection.") try: # Call the connection method. This gets overridden by a subclass if needed. self._mc_connect(host=self._mqtt_broker, port=self._mqtt_port) except Exception as e: - self._logger.warning("Could not connect to MQTT broker. Received exception '{}'".format(e)) + self._logger.warning(f"Could not connect to MQTT broker. {self._total_failures}/5 failures. Received exception '{e}'") + self._total_failures += 1 + if self._total_failures > 5: + self._logger.critical("Network: Too many network failures.") + raise return False # self._logger.debug("Network: MQTT connection attempt completed.") diff --git a/brickmaster2/network/circuitpython.py b/brickmaster2/network/circuitpython.py index 34abbf2..4a5e6bb 100644 --- a/brickmaster2/network/circuitpython.py +++ b/brickmaster2/network/circuitpython.py @@ -126,7 +126,18 @@ def _mc_publish(self, topic, message, qos=0, retain=False): :type retain: bool :return: None """ - self._mini_client.publish(topic, message, retain, qos) + try: + self._mini_client.publish(topic, message, retain, qos) + except OSError as e: + if e.args[0] == 104: + self._logger.error("Network: Tried to publish while not connected! Marking broker as not connected, " + "will retry.") + self._mqtt_connected = False + else: + raise e + except BrokenPipeError as e: + self._logger.error("Network: Disconnection while publishing! Marking broker as not connected, will retry.") + self._mqtt_connected = False def _mc_subscribe(self, topic): """ @@ -195,9 +206,9 @@ def _setup_mqtt(self): #TODO: Add option to enable and disable MQTT debugging separately. # If the overall Network module's logger is on at level debug, use that. - # if self._logger.getEffectiveLevel() == adafruit_logging.DEBUG: - # self._logger.debug("Network: Debug enabled, enabling logging on MQTT client as well.") - # self._mini_client.enable_logger(adafruit_logging, adafruit_logging.DEBUG, 'BrickMaster2') + if self._logger.getEffectiveLevel() == adafruit_logging.DEBUG: + self._logger.debug("Network: Debug enabled, enabling logging on MQTT client as well.") + self._mini_client.enable_logger(adafruit_logging, adafruit_logging.DEBUG, 'BrickMaster2') # Connect callback. self._mini_client.on_connect = self._on_connect diff --git a/circuitpy-code.py b/circuitpy-code.py index d651268..fb16d9d 100644 --- a/circuitpy-code.py +++ b/circuitpy-code.py @@ -46,6 +46,7 @@ except Exception as e: print("Received unhandled exception - ") traceback.print_exception(e) + print("Exception type: {}".format(type(e))) print("Waiting for 30s before.") time.sleep(30) microcontroller.reset() \ No newline at end of file From 42f3447cb7a6948e2d6543581a219d5b00b9f5ef Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 17 Jun 2024 15:08:08 -0400 Subject: [PATCH 2/7] New dev branch. Reorganizing for packaging. Add systemd unit file. --- examples/brickmaster2.service | 10 +++--- pyproject.toml | 36 +++++++++++++++++++ .../brickmaster2}/__init__.py | 0 .../brickmaster2}/cli/__init__.py | 0 .../brickmaster2}/cli/bm2cli.py | 0 {brickmaster2 => src/brickmaster2}/config.py | 0 .../brickmaster2}/controls.py | 0 {brickmaster2 => src/brickmaster2}/core.py | 0 {brickmaster2 => src/brickmaster2}/display.py | 0 .../brickmaster2}/exceptions.py | 0 .../brickmaster2}/indicators.py | 0 .../brickmaster2}/network/__init__.py | 0 .../brickmaster2}/network/base.py | 0 .../brickmaster2}/network/circuitpython.py | 0 .../brickmaster2}/network/linux.py | 0 .../brickmaster2}/network/mqtt.py | 0 .../brickmaster2}/network/wifi.py | 0 {brickmaster2 => src/brickmaster2}/scripts.py | 0 .../brickmaster2}/segment_format.py | 0 {brickmaster2 => src/brickmaster2}/util.py | 0 {brickmaster2 => src/brickmaster2}/version.py | 0 21 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 pyproject.toml rename {brickmaster2 => src/brickmaster2}/__init__.py (100%) rename {brickmaster2 => src/brickmaster2}/cli/__init__.py (100%) rename {brickmaster2 => src/brickmaster2}/cli/bm2cli.py (100%) rename {brickmaster2 => src/brickmaster2}/config.py (100%) rename {brickmaster2 => src/brickmaster2}/controls.py (100%) rename {brickmaster2 => src/brickmaster2}/core.py (100%) rename {brickmaster2 => src/brickmaster2}/display.py (100%) rename {brickmaster2 => src/brickmaster2}/exceptions.py (100%) rename {brickmaster2 => src/brickmaster2}/indicators.py (100%) rename {brickmaster2 => src/brickmaster2}/network/__init__.py (100%) rename {brickmaster2 => src/brickmaster2}/network/base.py (100%) rename {brickmaster2 => src/brickmaster2}/network/circuitpython.py (100%) rename {brickmaster2 => src/brickmaster2}/network/linux.py (100%) rename {brickmaster2 => src/brickmaster2}/network/mqtt.py (100%) rename {brickmaster2 => src/brickmaster2}/network/wifi.py (100%) rename {brickmaster2 => src/brickmaster2}/scripts.py (100%) rename {brickmaster2 => src/brickmaster2}/segment_format.py (100%) rename {brickmaster2 => src/brickmaster2}/util.py (100%) rename {brickmaster2 => src/brickmaster2}/version.py (100%) diff --git a/examples/brickmaster2.service b/examples/brickmaster2.service index 992fa06..091e310 100644 --- a/examples/brickmaster2.service +++ b/examples/brickmaster2.service @@ -1,12 +1,12 @@ [Unit] Description=Brickmaster2 Control System -After=default.target +Requires=sys-subsystem-net-devices-wlan0.device +After=sys-subsystem-net-devices-wlan0.device [Service] -Type=idle -WorkingDirectory=/home/pi/brickmaster2 -ExecStart=/usr/bin/python3 /home/pi/brickmaster2/brickmaster2.py -Restart=always +Type=simple +Environment="PYTHONPATH=/home/pi/brickmaster2-main" +ExecStart=/usr/bin/python3 /home/pi/brickmaster2-main/brickmaster2/cli/bm2cli.py -c /home/pi/lego_capitol.json [Install] WantedBy=default.target \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b4983e7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,36 @@ +[project] +name = "BrickMaster2" +dynamic = ["version"] +authors = [ + { name="Christopher Gill", email="chris@chrisgill.net" }, +] +description = "CobraBay Parking Guidance System" +readme = "README.md" +requires-python = ">=3.8" +dependencies = [ + "adafruit-circuitpython-ht16k33", + "adafruit-circuitpython-logging", + "Adafruit-Blinka", + "netifaces2", + "paho-mqtt", + "psutil", + "pid" +] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Development Status :: 3 - Alpha", + "Topic :: Home Automation" +] + +[project.urls] +Homepage = "https://github.com/chrisgilldc/cobrabay" +Issues = "https://github.com/chrisgilldc/cobrabay/issues" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.version] +path = "pkg/__about__.py" \ No newline at end of file diff --git a/brickmaster2/__init__.py b/src/brickmaster2/__init__.py similarity index 100% rename from brickmaster2/__init__.py rename to src/brickmaster2/__init__.py diff --git a/brickmaster2/cli/__init__.py b/src/brickmaster2/cli/__init__.py similarity index 100% rename from brickmaster2/cli/__init__.py rename to src/brickmaster2/cli/__init__.py diff --git a/brickmaster2/cli/bm2cli.py b/src/brickmaster2/cli/bm2cli.py similarity index 100% rename from brickmaster2/cli/bm2cli.py rename to src/brickmaster2/cli/bm2cli.py diff --git a/brickmaster2/config.py b/src/brickmaster2/config.py similarity index 100% rename from brickmaster2/config.py rename to src/brickmaster2/config.py diff --git a/brickmaster2/controls.py b/src/brickmaster2/controls.py similarity index 100% rename from brickmaster2/controls.py rename to src/brickmaster2/controls.py diff --git a/brickmaster2/core.py b/src/brickmaster2/core.py similarity index 100% rename from brickmaster2/core.py rename to src/brickmaster2/core.py diff --git a/brickmaster2/display.py b/src/brickmaster2/display.py similarity index 100% rename from brickmaster2/display.py rename to src/brickmaster2/display.py diff --git a/brickmaster2/exceptions.py b/src/brickmaster2/exceptions.py similarity index 100% rename from brickmaster2/exceptions.py rename to src/brickmaster2/exceptions.py diff --git a/brickmaster2/indicators.py b/src/brickmaster2/indicators.py similarity index 100% rename from brickmaster2/indicators.py rename to src/brickmaster2/indicators.py diff --git a/brickmaster2/network/__init__.py b/src/brickmaster2/network/__init__.py similarity index 100% rename from brickmaster2/network/__init__.py rename to src/brickmaster2/network/__init__.py diff --git a/brickmaster2/network/base.py b/src/brickmaster2/network/base.py similarity index 100% rename from brickmaster2/network/base.py rename to src/brickmaster2/network/base.py diff --git a/brickmaster2/network/circuitpython.py b/src/brickmaster2/network/circuitpython.py similarity index 100% rename from brickmaster2/network/circuitpython.py rename to src/brickmaster2/network/circuitpython.py diff --git a/brickmaster2/network/linux.py b/src/brickmaster2/network/linux.py similarity index 100% rename from brickmaster2/network/linux.py rename to src/brickmaster2/network/linux.py diff --git a/brickmaster2/network/mqtt.py b/src/brickmaster2/network/mqtt.py similarity index 100% rename from brickmaster2/network/mqtt.py rename to src/brickmaster2/network/mqtt.py diff --git a/brickmaster2/network/wifi.py b/src/brickmaster2/network/wifi.py similarity index 100% rename from brickmaster2/network/wifi.py rename to src/brickmaster2/network/wifi.py diff --git a/brickmaster2/scripts.py b/src/brickmaster2/scripts.py similarity index 100% rename from brickmaster2/scripts.py rename to src/brickmaster2/scripts.py diff --git a/brickmaster2/segment_format.py b/src/brickmaster2/segment_format.py similarity index 100% rename from brickmaster2/segment_format.py rename to src/brickmaster2/segment_format.py diff --git a/brickmaster2/util.py b/src/brickmaster2/util.py similarity index 100% rename from brickmaster2/util.py rename to src/brickmaster2/util.py diff --git a/brickmaster2/version.py b/src/brickmaster2/version.py similarity index 100% rename from brickmaster2/version.py rename to src/brickmaster2/version.py From f70d8e2228328b768411f00c8f52c70447de478a Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 19 Jun 2024 09:39:14 -0400 Subject: [PATCH 3/7] Cleaning up. --- .idea/brickmaster2.iml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .idea/brickmaster2.iml diff --git a/.idea/brickmaster2.iml b/.idea/brickmaster2.iml deleted file mode 100644 index e2b102d..0000000 --- a/.idea/brickmaster2.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file From ebb447f8c44f36ec87c6d9695ca8b8ff4c198135 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 19 Jun 2024 10:40:45 -0400 Subject: [PATCH 4/7] Base: Clean up some error/log reporting. Circuitpython: Reorder exception catching based on inheritance Otherwise general comment cleanup. Reversion clearly as alpha1. --- src/brickmaster2/network/base.py | 6 ++--- src/brickmaster2/network/circuitpython.py | 14 +++++++----- src/brickmaster2/network/mqtt.py | 1 + src/brickmaster2/util.py | 27 +++++------------------ src/brickmaster2/version.py | 2 +- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/brickmaster2/network/base.py b/src/brickmaster2/network/base.py index ec8b1a1..060295d 100644 --- a/src/brickmaster2/network/base.py +++ b/src/brickmaster2/network/base.py @@ -256,13 +256,14 @@ def _connect_mqtt(self): # Call the connection method. This gets overridden by a subclass if needed. self._mc_connect(host=self._mqtt_broker, port=self._mqtt_port) except Exception as e: - self._logger.warning(f"Could not connect to MQTT broker. {self._total_failures}/5 failures. Received exception '{e}'") + self._logger.warning(f"Network: Could not connect to MQTT broker. {self._total_failures}/5 failures. Received exception '{e}'") + self._logger.debug(f"Network: Exception is type '{type(e)}', args is '{e.args}'") self._total_failures += 1 if self._total_failures > 5: self._logger.critical("Network: Too many network failures.") raise return False - # self._logger.debug("Network: MQTT connection attempt completed.") + self._logger.debug("Network: MQTT connection attempt completed.") # Set the internal MQTT tracker to True. Surprisingly, the client doesn't have a way to track this itself! # self._logger.debug("Network: Setting internal MQTT tracker True in '_connect_mqtt' call.") @@ -439,7 +440,6 @@ def _pub_message(self, topic, message, force_repeat=False): else: outbound_message = message # Make the client-specific call! - self._mc_publish(topic, outbound_message) # Method studs to be overridden. diff --git a/src/brickmaster2/network/circuitpython.py b/src/brickmaster2/network/circuitpython.py index 4a5e6bb..00a15a9 100644 --- a/src/brickmaster2/network/circuitpython.py +++ b/src/brickmaster2/network/circuitpython.py @@ -89,9 +89,8 @@ def _mc_platform_messages(self): Platform-specific MQTT messages. :return: list """ - #TODO: Rewrite for CircuitPython - # On Linux we use PSUtil for this. Here we can use the - # CircuitPython gc doesn't have all the methods psutil does, so we have to do some math. + # On Linux we use PSUtil for this. Here we use the CircuitPython garbage collector (gc), which doesn't have + # all the same convenience methods psutil does, so we have to do some math. return_dict = { 'topic': 'brickmaster2/' + self._short_name + '/meminfo', 'message': { 'mem_avail': 'Unknown', 'mem_total': 'Unknown', 'pct_used': 'Unknown', @@ -128,6 +127,12 @@ def _mc_publish(self, topic, message, qos=0, retain=False): """ try: self._mini_client.publish(topic, message, retain, qos) + except BrokenPipeError as e: + self._logger.error("Network: Disconnection while publishing! Marking broker as not connected, will retry.") + self._mqtt_connected = False + except ConnectionError as e: + self._logger.error("Network: Connection failed, raised error '{}'".format(e.args[0])) + self._mqtt_connected = False except OSError as e: if e.args[0] == 104: self._logger.error("Network: Tried to publish while not connected! Marking broker as not connected, " @@ -135,9 +140,6 @@ def _mc_publish(self, topic, message, qos=0, retain=False): self._mqtt_connected = False else: raise e - except BrokenPipeError as e: - self._logger.error("Network: Disconnection while publishing! Marking broker as not connected, will retry.") - self._mqtt_connected = False def _mc_subscribe(self, topic): """ diff --git a/src/brickmaster2/network/mqtt.py b/src/brickmaster2/network/mqtt.py index 2ffc607..0d5238a 100644 --- a/src/brickmaster2/network/mqtt.py +++ b/src/brickmaster2/network/mqtt.py @@ -124,6 +124,7 @@ def ha_discovery(short_name, system_id, device_info, topic_prefix, ha_base, memi outbound_messages.extend(ha_discovery_control( short_name, system_id, device_info, topic_prefix, ha_base, object_registry['controls'][control_id])) + #TODO: Add items for scripts as a config option. # The outbound topics dict includes references to the objects, so we can get the objects from there. # for item in self._topics_outbound: # if isinstance(self._topics_outbound[item]['obj'], brickmaster2.controls.CtrlGPIO): diff --git a/src/brickmaster2/util.py b/src/brickmaster2/util.py index 92c01e8..4df3341 100644 --- a/src/brickmaster2/util.py +++ b/src/brickmaster2/util.py @@ -31,31 +31,16 @@ def load_config(config_path): return the_json -def mac_id(wifihw=None): +def mac_id(wifihw='wlan0'): """ - Get the MAC ID of the default gateway interface for a Linux system. + Get the MAC ID of the default gateway interface for a Linux system. Circuitpython doesn't need to use this method, + as MAC is retrieved by the wifi class which is invoked by code.py prior to creating the Brickmaster instance. + :return: """ - # getmac = __import__('getmac') # #TODO: Replace this with actually checking against the default route. May be too many edge cases. - # mac = getmac.get_mac_address(interface="wlan0") - # return mac.replace(':', '') - if sys.implementation.name == 'cpython': - mac = netifaces.ifaddresses('wlan0')[netifaces.AF_PACKET][0]['addr'] - return mac.replace(':', '') - elif sys.implementation.name == 'circuitpython': - #TODO: Test for Circuitpython MACs. - # Try to auto-determine Wifi hardware if not explicitly set. - if wifihw is None: - pass - if wifihw == 'esp32': - pass - elif wifihw == 'esp32spi': - pass - else: - raise NotImplemented("Cannot determine platform!") - else: - raise NotImplemented("Unknown platform!") + mac = netifaces.ifaddresses(wifihw)[netifaces.AF_PACKET][0]['addr'] + return mac.replace(':', '') def determine_wifi_hw(): """ diff --git a/src/brickmaster2/version.py b/src/brickmaster2/version.py index 9710278..cce0fa8 100644 --- a/src/brickmaster2/version.py +++ b/src/brickmaster2/version.py @@ -1,2 +1,2 @@ """ Brickmaster2 Version """ -__version__ = "0.5.0-beta" +__version__ = "0.5.0alpha1" From 9080aef7118cb63117e2505a833f274e694be385 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 19 Jun 2024 11:16:43 -0400 Subject: [PATCH 5/7] 0.5.0alpha2 Update wifi settings so circuitpython can set hostname in DHCP requests from the CIRCUITPY_WEB_INSTANCE_NAME environment variable in settings.toml. This will make it easier to find a particular device among many. --- circuitpy-code.py | 13 +++++++++++-- src/brickmaster2/network/wifi.py | 18 +++++++++++++----- src/brickmaster2/version.py | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/circuitpy-code.py b/circuitpy-code.py index fb16d9d..5e9c7dc 100644 --- a/circuitpy-code.py +++ b/circuitpy-code.py @@ -11,11 +11,19 @@ # Check for the WIFI HW environment setting. try: - wifihw = os.getenv("WIFI_HW") + wifihw = os.getenv("BRICKMASTER_WIFI_HW") print("Wireless hardware pre-defined as '{}'".format(wifihw)) except AttributeError: + print("Wireless hardware not specified, auto-determining.") wifihw = brickmaster2.util.determine_wifi_hw() +# Check for a hostname +try: + hostname = os.getenv("CIRCUITPY_WEB_INSTANCE_NAME") +except AttributeError: + print("Hostname not available. Set with 'CIRCUITPY_WEB_INSTANCE_NAME' in 'settings.toml'.") + hostname = None + # Open the config file. #TODO: Replace this with fancier config open logic. print("Loading 'config.json'") @@ -28,7 +36,8 @@ wifi_obj = brickmaster2.network.BM2WiFi( ssid=os.getenv("CIRCUITPY_WIFI_SSID"), password=os.getenv("CIRCUITPY_WIFI_PASSWORD"), - wifihw=wifihw + wifihw=wifihw, + hostname = hostname ) # Create the BrickMaster2 Object. diff --git a/src/brickmaster2/network/wifi.py b/src/brickmaster2/network/wifi.py index e2085ff..2bf9641 100644 --- a/src/brickmaster2/network/wifi.py +++ b/src/brickmaster2/network/wifi.py @@ -12,7 +12,8 @@ class BM2WiFi: """ BrickMaster2 WiFi Handling for CircuitPython Boards """ - def __init__(self, ssid, password, wifihw=None, retry_limit = 5, retry_time = 30, log_level=adafruit_logging.DEBUG): + def __init__(self, ssid, password, wifihw=None, retry_limit = 5, retry_time = 30, hostname = None, + log_level=adafruit_logging.DEBUG): """ Set up the BrickMaster2 WiFi handler. Works for ESP32s, direct or SPI connected. @@ -26,12 +27,15 @@ def __init__(self, ssid, password, wifihw=None, retry_limit = 5, retry_time = 30 :type retry_limit: int :param retry_time: How long to wait between retries, in seconds. :type retry_time: int + :param hostname: Hostname to set. Otherwise will default to whatever the board wants. + :type hostname: str :param log_level: """ # Create the logger and set the level to debug. This will get reset later. self._logger = adafruit_logging.getLogger("BrickMaster2") self._logger.setLevel(log_level) + self._hostname = hostname self._mac_string = None self._password = password self._retry_limit = retry_limit @@ -159,13 +163,15 @@ def _setup_wifi(self): global wifi import wifi # Do the setup. - self._logger.info("Network: Configuring Native ESP32...") + self._logger.info("Wifi: Configuring Native ESP32...") self._wifi = wifi.radio self._mac_string = "{:X}{:X}{:X}{:X}{:X}{:X}". \ format(self._wifi.mac_address[0], self._wifi.mac_address[1], self._wifi.mac_address[2], self._wifi.mac_address[3], self._wifi.mac_address[4], self._wifi.mac_address[5]) # Set the hostname - # self._wifi.hostname(self._system_name) + if self._hostname is not None: + self._logger.info(f"Wifi: Setting hostname to '{self._hostname}'") + self._wifi.hostname = self._hostname elif self._wifihw == 'esp32spi': # Conditional imports for ESP32SPI boards. ## Board @@ -201,13 +207,15 @@ def _setup_wifi(self): self._logger.warning("WIFI: ESP32 co-processor busy. Resetting!") supervisor.reload() time.sleep(5) - self._logger.info("Network: ESP32 Firmware version is '{}.{}.{}'".format( + self._logger.info("Wifi: ESP32 Firmware version is '{}.{}.{}'".format( self._wifi.firmware_version[0], self._wifi.firmware_version[1], self._wifi.firmware_version[2])) self._mac_string = "{:X}{:X}{:X}{:X}{:X}{:X}".format( self._wifi.MAC_address[5], self._wifi.MAC_address[4], self._wifi.MAC_address[3], self._wifi.MAC_address[2], self._wifi.MAC_address[1], self._wifi.MAC_address[0]) # # Set the hostname - # self._wifi.set_hostname(self._system_id) + if self._hostname is not None: + self._logger.debug(f"Wifi: Setting hostname to {self._hostname}") + self._wifi.set_hostname(self._hostname) # self._logger.info("WIFI: Set hostname to '{}'".format(self._system_id)) else: raise ValueError("WIFI: Hardware type '{}' not supported.".format(self._wifihw)) diff --git a/src/brickmaster2/version.py b/src/brickmaster2/version.py index cce0fa8..f449468 100644 --- a/src/brickmaster2/version.py +++ b/src/brickmaster2/version.py @@ -1,2 +1,2 @@ """ Brickmaster2 Version """ -__version__ = "0.5.0alpha1" +__version__ = "0.5.0alpha2" From 46f5a4a643b97f76eca314d657cd23eda435fca4 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 19 Jun 2024 11:31:07 -0400 Subject: [PATCH 6/7] Add a config for the BrickmasterXL hardware. Fix the name of the Saturn 5 Full script. --- hwconfigs/brickmasterXL.json | 147 +++++++++++++++++++++++++++++++++++ scripts/saturn5_full.json | 2 +- 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 hwconfigs/brickmasterXL.json diff --git a/hwconfigs/brickmasterXL.json b/hwconfigs/brickmasterXL.json new file mode 100644 index 0000000..b9a005a --- /dev/null +++ b/hwconfigs/brickmasterXL.json @@ -0,0 +1,147 @@ +{ + "system": { + "id": "brickmasterxl", + "name": "BrickmasterXL", + "log_level": "warning", + "ha":{ + "area": ### Area name here is recommended but not required. ### + }, + "mqtt": { + "broker": ### Broker hostname or IP ### + "user": ### MQTT Username ###, + "key": ### MQTT Password ### + } + }, + "controls": [ + { + "id": "1_5v", + "name": "Port 1 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 11 + }, + { + "id": "2_5v", + "name": "Port 2 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 10 + }, + { + "id": "3_5v", + "name": "Port 3 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 9, + "disable": true + }, + { + "id": "4_5v", + "name": "Port 4 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 8, + "disable": true + }, + { + "id": "5_9v", + "name": "Port 5 - 9v", + "type": "gpio", + "pin": "D5", + "invert": true, + "disable": false + }, + { + "id": "6_9v", + "name": "Port 6 - 9v", + "type": "gpio", + "pin": "D6", + "invert": true, + "disable": false + }, + { + "id": "7_9v", + "name": "Port 7 - 9v", + "type": "gpio", + "pin": "D7", + "invert": true, + "disable": true + }, + { + "id": "8_9v", + "name": "Port 8 - 9v", + "type": "gpio", + "pin": "D8", + "invert": "true", + "disable": true + }, + { + "id": "9_5v", + "name": "Port 9 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 4, + "disable": true + }, + { + "id": "10_5v", + "name": "Port 10 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 3, + "disable": true + }, + { + "id": "11_5v", + "name": "Port 11 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 2, + "disable": true + }, + { + "id": "12_5v", + "name": "Port 12 - 5v", + "type": "aw9523", + "addr": "0x58", + "pin": 1, + "disable": true + }, + { + "id": "13_9v", + "name": "Port 13 - 9v", + "type": "gpio", + "pin": "D4", + "invert": true, + "disable": true + }, + { + "id": "14_9v", + "name": "Port 14 - 9v", + "type": "gpio", + "pin": "D3", + "invert": true, + "disable": true + }, + { + "id": "15_9v", + "name": "Port 15 - 9v", + "type": "gpio", + "pin": "D2", + "invert": true, + "disable": true + }, + { + "id": "16_9v", + "name": "Port 16 - 9v", + "type": "gpio", + "pin": "D1", + "invert": true, + "disable": true + } + ], + "displays": [ + ], + "scripts": { + } +} diff --git a/scripts/saturn5_full.json b/scripts/saturn5_full.json index 089a02a..60876e0 100644 --- a/scripts/saturn5_full.json +++ b/scripts/saturn5_full.json @@ -1,6 +1,6 @@ { "id": "saturn5_full", - "name": "Saturn 5 Full Launch", + "name": "Saturn V Full Launch", "type": "flight", "run": "once", "at_completion": "restore", From fe8a28a2949544eb61e0da4503104d948b1af9d2 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 27 Jun 2024 06:28:43 -0400 Subject: [PATCH 7/7] 0.5.0-alpha3 Fix setting Icon in control configuration. Icon customization now works! --- src/brickmaster2/config.py | 19 ++++++++++++------- src/brickmaster2/version.py | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/brickmaster2/config.py b/src/brickmaster2/config.py index 14cd059..0064a08 100644 --- a/src/brickmaster2/config.py +++ b/src/brickmaster2/config.py @@ -218,16 +218,17 @@ def _validate_secrets(self): def _validate_controls(self): if not isinstance(self._config['controls'], list): - self._logger.critical('Controls not correctly defined. Must be a list of dictionaries.') + self._logger.critical('Config: Controls not correctly defined. Must be a list of dictionaries.') return i = 0 to_delete = [] while i < len(self._config['controls']): + self._logger.debug("Config: Validating control definition '{}'".format(self._config['controls'][i])) # Check to see if required items are defined. required_keys = ['id', 'type'] for key in required_keys: if key not in self._config['controls'][i]: - self._logger.critical("Required control config option '{}' missing in control {}. Cannot configure!". + self._logger.critical("Config: Required control config option '{}' missing in control {}. Cannot configure!". format(key, i+1)) to_delete.append(i) @@ -257,14 +258,14 @@ def _validate_controls(self): elif ctrltype == 'aw9523': required_parameters = ['addr', 'pin'] else: - self._logger.error("Cannot set up control '{}', type '{}' is not supported.".format(i, ctrltype)) + self._logger.error("Config: Cannot set up control '{}', type '{}' is not supported.".format(i, ctrltype)) to_delete.append(i) i += 1 continue for req_param in required_parameters: if req_param not in self._config['controls'][i]: - self._logger.error("Cannot set up control '{}', no '{}' directive.".format( + self._logger.error("Config: Cannot set up control '{}', no '{}' directive.".format( self._config['controls'][i]['name'], req_param)) to_delete.append(i) i += 1 @@ -275,11 +276,15 @@ def _validate_controls(self): 'icon': 'mdi:toy-brick' } for param in optional_params: - self._logger.debug("Checking for optional parameter '{}'".format(param)) - if param not in self._config['system']: - self._logger.warning("Option '{}' not found, using default '{}'". + self._logger.debug("Config: Checking for optional parameter '{}'".format(param)) + if param not in self._config['controls'][i]: + self._logger.warning("Config: Option '{}' not found, using default '{}'". format(param, optional_defaults[param])) self._config['controls'][i][param] = optional_defaults[param] + else: + self._logger.debug("Config: Optional parameter '{}' set to '{}'".format( + param, self._config['controls'][i][param] + )) i += 1 # Make the to_delete list unique. diff --git a/src/brickmaster2/version.py b/src/brickmaster2/version.py index f449468..7d9750f 100644 --- a/src/brickmaster2/version.py +++ b/src/brickmaster2/version.py @@ -1,2 +1,2 @@ """ Brickmaster2 Version """ -__version__ = "0.5.0alpha2" +__version__ = "0.5.0alpha3"