async def publish_properties(self): """publish device and node properties""" publish = self.publish # device properties await publish("$homie", "4.0.0") await publish("$name", self.device_name) await publish(DEVICE_STATE, STATE_INIT) await publish("$implementation", bytes(platform, UTF8)) await publish( "$nodes", ",".join([n.id for n in self.nodes]) ) # node properties nodes = self.nodes for n in nodes: await n.publish_properties() if self._extensions: await publish("$extensions", ",".join(self._extensions)) if "org.homie.legacy-firmware:0.1.1:[4.x]" in self._extensions: await publish("$localip", utils.get_local_ip()) await publish("$mac", utils.get_local_mac()) await publish("$fw/name", "Microhomie") await publish("$fw/version", __version__) if "org.homie.legacy-stats:0.1.1:[4.x]" in self._extensions: await self.publish("$stats/interval", str(self.stats_interval)) # Start stats coro launch(self.publish_stats, ())
async def killer(self): loop = asyncio.get_event_loop() while self.tstop > loop.time(): # Must loop here: might be retriggered await asyncio.sleep_ms(self.tstop - loop.time()) if self._running and self.func is not None: launch(self.func, self.args) # Execute callback self._running = False
async def killer(self): loop = asyncio.get_event_loop() twait = time.ticks_diff(self.tstop, loop.time()) while twait > 0 and self._running: # Return if stop() called during wait # Must loop here: might be retriggered await asyncio.sleep_ms(twait) twait = time.ticks_diff(self.tstop, loop.time()) if self._running and self.func is not None: launch(self.func, self.args) # Execute callback self._running = False
async def killer(self): twait = time.ticks_diff(self.tstop, time.ticks_ms()) while twait > 0: # Must loop here: might be retriggered await asyncio.sleep_ms(twait) if self.tstop is None: break # Return if stop() called during wait twait = time.ticks_diff(self.tstop, time.ticks_ms()) if self.tstop is not None and self.func is not None: launch(self.func, self.args) # Timed out: execute callback self.tstop = None # Not running
async def connection_handler(self, client): """subscribe to all registered device and node topics""" if self._first_start is False: await self.publish(DEVICE_STATE, STATE_RECOVER) retained = [] subscribe = self.subscribe # Broadcast topic await self.mqtt.subscribe("{}/{}/#".format(self.btopic, T_BC), QOS) # Micropython extension await self.mqtt.subscribe("{}/{}".format(self.dtopic, T_MPY), QOS) # node topics nodes = self.nodes for n in nodes: props = n._properties for pid, p in props.items(): if p.restore: # Restore from topic with retained message await self.add_node_cb(n) t = "{}/{}".format(n.id, pid) await subscribe(t) retained.append(t) if p.settable: await self.add_node_cb(n) await subscribe("{}/{}/set".format(n.id, pid)) # on first connection: # * publish device and node properties # * enable WDT # * run all coros if self._first_start is True: await self.publish_properties() unsubscribe = self.unsubscribe # unsubscribe from retained topics for t in retained: await unsubscribe(t) self._first_start = False # activate WDT if LINUX is False and self.debug is False: launch(self.wdt, ()) # start coros waiting for ready state _EVENT.set() await sleep_ms(MAIN_DELAY) _EVENT.clear() await self.publish(DEVICE_STATE, STATE_READY)
async def switchcheck(self): loop = asyncio.get_event_loop() while True: state = self.pin.value() if state != self.switchstate: # State has changed: act on it now. self.switchstate = state if state == 0 and self.close_func: launch(self._close_func, self._close_args) elif state == 1 and self._open_func: launch(self._open_func, self._open_args) # Ignore further state changes until switch has settled await asyncio.sleep_ms(Switch.debounce_ms)
def on_active_msg(self, topic, payload, retained): if payload == FALSE: if self.active: asyn.launch(asyn.NamedTask.cancel, (_SENSOR,)) self.active = False elif payload == TRUE: if not self.active: self.active = True loop = get_event_loop() loop.create_task( asyn.NamedTask(_SENSOR, self.pir_sensor)() ) else: return self.active_property.data = payload
def callback(self, topic, payload, retained): if b"active" in topic: if payload == FALSE: if self.active: asyn.launch(asyn.NamedTask.cancel, ("pir_sensor", )) self.active = False elif payload == TRUE: if not self.active: self.active = True loop = get_event_loop() loop.create_task( asyn.NamedTask("pir_sensor", self.pir_sensor)()) else: return self.pir_property.data = payload if retained: self.pir_sensor.update_delta()
def callback(self, topic, payload, retained): """ Gets called when a payload arrive on node topics This method is keeped for backward compatibilty. Old Microhomie versions still can overwrite this method in child classes to handle mesages. """ # unsubscribe from retained topic if retained: launch(self.device.unsubscribe, (topic, )) t = topic.split(SLASH) pid = t.pop() # property id if pid == SET: pid = t.pop() try: p = self._properties[pid] p.msg_handler(topic, payload, retained) except KeyError: pass
async def buttoncheck(self): loop = asyncio.get_event_loop() if self._long_func: longdelay = Delay_ms(self._long_func, self._long_args) if self._double_func: doubledelay = Delay_ms() while True: state = self.rawstate() # State has changed: act on it now. if state != self.buttonstate: self.buttonstate = state if state: # Button is pressed if self._long_func and not longdelay.running(): # Start long press delay longdelay.trigger(Pushbutton.long_press_ms) if self._double_func: if doubledelay.running(): launch(self._double_func, self._double_args) else: # First click: start doubleclick timer doubledelay.trigger(Pushbutton.double_click_ms) if self._true_func: launch(self._true_func, self._true_args) else: # Button release if self._long_func and longdelay.running(): # Avoid interpreting a second click as a long push longdelay.stop() if self._false_func: launch(self._false_func, self._false_args) # Ignore state changes until switch has settled await asyncio.sleep_ms(Pushbutton.debounce_ms)
def sub_cb(self, topic, payload, retained): topic = topic.decode() payload = payload.decode() self.dprint( "MQTT MESSAGE: {} --> {}, {}".format(topic, payload, retained) ) # Only non-retained messages are allowed on /set topics if retained and topic.endswith(T_SET): return # broadcast callback passed to nodes if T_BC in topic: nodes = self.nodes for n in nodes: n.broadcast_callback(topic, payload, retained) # Micropython extension elif topic.endswith(T_MPY): if payload == "reset": launch(self.reset, ("reset",)) elif payload == "webrepl": launch(self.reset, ("webrepl",)) elif payload == "yaota8266": launch(self.reset, ("yaotaota",)) else: # node property callbacks nt = topic.split(SLASH) node = nt[len(self.dtopic.split(SLASH))] if node in self.callback_topics: self.callback_topics[node](topic, payload, retained)
def add_node(self, node): """add a node class of Homie Node to this device""" collect() node.device = self self.nodes.append(node) launch(node.publish_data, ())