def __init__(self, id=0, timeout=120, use_rtc_memory=True): self._timeout = timeout / 10 self._counter = 0 self._timer = machine.Timer(id) self._use_rtc_memory = use_rtc_memory self._has_filesystem = False self.init() asyncio.get_event_loop().create_task(self._resetCounter()) if sys_vars.hasFilesystem(): self._has_filesystem = True try: with open("watchdog.txt", "r") as f: if f.read() == "True": logging.getLogger("WDT").warn("Reset reason: Watchdog") except Exception as e: print(e) # file probably just does not exist try: with open("watchdog.txt", "w") as f: f.write("False") except Exception as e: logging.getLogger("WDT").error("Error saving to file: {!s}".format(e)) elif use_rtc_memory and platform == "esp8266": rtc = machine.RTC() if rtc.memory() == b"WDT reset": logging.getLogger("WDT").critical("Reset reason: Watchdog") rtc.memory(b"")
async def _buildComponents(self, topic=None, msg=None, retain=None): log.debug("Building components", local_only=True) loop = asyncio.get_event_loop() await self.unsubscribe("{!s}/login/".format(self.mqtt_home) + self.id) if type(msg) != dict: log.critical("Received config is no dict") msg = None if msg is None: log.error("No configuration received, falling back to last saved config") loop.create_task(config.loadComponentsFile()) else: log.info("received config: {!s}".format(msg), local_only=True) # saving components config.saveComponentsFile(msg) if platform == "esp8266": # on esp8266 components are split in small files and loaded after each other # to keep RAM requirements low, only if filesystem is enabled if sys_vars.hasFilesystem(): loop.create_task(config.loadComponentsFile()) else: loop.create_task(config.registerComponentsAsync(msg)) else: # on esp32 components are registered directly but async to let logs run between registrations loop.create_task(config.registerComponentsAsync(msg)) self.__receive_config = None
async def _receiveConfig(self): self.__receive_config = None while True: gc.collect() _log.debug("RAM before receiveConfig import: {!s}".format( gc.mem_free()), local_only=True) import pysmartnode.networking.mqtt_receive_config gc.collect() _log.debug("RAM after receiveConfig import: {!s}".format( gc.mem_free()), local_only=True) result = await pysmartnode.networking.mqtt_receive_config.requestConfig( config, self, _log) if result is False: _log.info("Using local components.json/py", local_only=True) gc.collect() _log.debug("RAM before receiveConfig deletion: {!s}".format( gc.mem_free()), local_only=True) del pysmartnode.networking.mqtt_receive_config del sys.modules["pysmartnode.networking.mqtt_receive_config"] gc.collect() _log.debug("RAM after receiveConfig deletion: {!s}".format( gc.mem_free()), local_only=True) local_works = await config._loadComponentsFile() if local_works is True: return True else: gc.collect() _log.debug("RAM before receiveConfig deletion: {!s}".format( gc.mem_free()), local_only=True) del pysmartnode.networking.mqtt_receive_config del sys.modules["pysmartnode.networking.mqtt_receive_config"] gc.collect() _log.debug("RAM after receiveConfig deletion: {!s}".format( gc.mem_free()), local_only=True) result = ujson.loads(result) loop = asyncio.get_event_loop() if platform == "esp8266": # on esp8266 components are split in small files and loaded after each other # to keep RAM requirements low, only if filesystem is enabled if sys_vars.hasFilesystem(): loop.create_task(config._loadComponentsFile()) else: loop.create_task( config._registerComponentsAsync(result)) else: # on esp32 components are registered directly but async to let logs run between registrations loop.create_task(config._registerComponentsAsync(result)) return True await asyncio.sleep( 60 ) # if connection not stable or broker unreachable, try again in 60s
def _wdt(self, t): self._counter += self._timeout if self._counter >= self._timeout * 10: if sys_vars.hasFilesystem(): try: with open("watchdog.txt", "w") as f: f.write("True") except Exception as e: print("Error saving to file: {!s}".format(e)) machine.reset()
async def loadComponentsFile(_log, registerComponentsAsync): if not sys_vars.hasFilesystem(): comps = _importComponents(_log) if comps is False: _log.critical( "Can't load components file as filesystem is unavailable") return False return comps try: f = open("components.json", "r") components_found = True except OSError: components_found = False if components_found is False: try: f = open("_order.json", "r") except Exception as e: # if loading configuration jsons fails, try to import components.py comps = _importComponents(_log) if comps is False: _log.critical("_order.json does not exist, {!s}".format(e)) return False else: return comps order = ujson.loads(f.read()) f.close() gc.collect() for component in order: tmp = {"_order": [component]} try: f = open("components/{!s}.json".format(component), "r") tmp[component] = ujson.loads(f.read()) f.close() await registerComponentsAsync(tmp) except Exception as e: _log.error("Error loading component file {!s}, {!s}".format( component, e)) gc.collect() if platform == "esp8266": await asyncio.sleep(1) # gives time to get retained topic and settle ram, important on esp8266 else: await asyncio.sleep_ms(100) gc.collect() return True else: c = f.read() f.close() try: c = ujson.loads(c) gc.collect() return c except Exception as e: _log.critical("components.json parsing error {!s}".format(e)) return False
def __init__(self, receive_config=False, allow_wildcards=True): """ receive_config: False, if true tries to get the configuration of components from a server connected to the mqtt broker allow_wildcards: True, if false no subscriptions ending with "/#" are allowed; this also saves RAM as the module "subscription" is used as a backend to store subscriptions instead of the module "tree" which is bigger """ if platform == "esp8266" and sys_vars.hasFilesystem(): """ esp8266 has very limited RAM so choosing a module that allows wildcards but writes subscribed topics to a file if filesystem is enabled, else uses Tree. - less feature and less general + specifically made for mqtt and esp8266 - makes it a lot slower (~30ms checking a subscription, ~120ms saving one) + saves at least 1kB with a few subscriptions """ from pysmartnode.utils.subscriptionHandlers.subscribe_file import SubscriptionHandler self._subscriptions = SubscriptionHandler() else: """ esp32 has a lot of RAM but if wildcards are not needed then the subscription module is faster and saves ram but does not support wildcards. For wildcard support the module tree is used. Also used for esp8266 with no filesystem (which saves ~6kB) """ if allow_wildcards: from pysmartnode.utils.subscriptionHandlers.tree import Tree gc.collect() self._subscriptions = Tree(config.MQTT_HOME, ["Functions"]) else: from pysmartnode.utils.subscriptionHandlers.subscription import SubscriptionHandler gc.collect() self._subscriptions = SubscriptionHandler(["Functions"]) self._allow_wildcards = allow_wildcards self.payload_on = ("ON", True, "True") self.payload_off = ("OFF", False, "False") self._retained = [] self.id = config.id self.mqtt_home = config.MQTT_HOME super().__init__(server=config.MQTT_HOST, port=1883, user=config.MQTT_USER, password=config.MQTT_PASSWORD, keepalive=config.MQTT_KEEPALIVE, subs_cb=self._execute_sync, wifi_coro=self._wifiChanged, connect_coro=self._connected, will=(self.getRealTopic(self.getDeviceTopic("status")), "OFFLINE", True, 1), clean=False, ssid=config.WIFI_SSID, wifi_pw=config.WIFI_PASSPHRASE) asyncio.get_event_loop().create_task(self.connect()) self.__receive_config = receive_config
def __init__(self, id=0, timeout=120): self._timeout = timeout / 10 self._counter = 0 self._timer = machine.Timer(id) self.init() asyncio.get_event_loop().create_task(self._resetCounter()) if sys_vars.hasFilesystem(): try: with open("watchdog.txt", "r") as f: if f.read() == "True": log.warn("Reset reason: Watchdog") except Exception as e: print(e) # file probably just does not exist try: with open("watchdog.txt", "w") as f: f.write("False") except Exception as e: log.error("Error saving to file: {!s}".format(e))
def _saveComponentsFile(msg): from pysmartnode.utils import sys_vars from sys import platform import os import gc if not sys_vars.hasFilesystem(): _log.debug("Not saving components as filesystem is unavailable", local_only=True) return if platform == "esp8266": tmp = ujson.dumps(msg["_order"]) f = open("_order.json", "w") f.write(tmp) f.close() del tmp try: os.mkdir("components") except Exception as e: # probably already there f = os.listdir("components") for file in f: os.remove("components/" + file) del f gc.collect() for component in msg: if component != "_order": try: f = open("components/{!s}.json".format(component), "w") f.write(ujson.dumps(msg[component])) f.close() except Exception as e: _log.error("Can't save component {!s}, {!s}".format( component, e)) try: os.remove("components.json") except Exception as e: pass else: tmp = ujson.dumps(msg) f = open("components.json", "w") f.write(tmp) f.close()