diff --git a/doc/devices_classes_branch.txt b/doc/devices_classes_branch.txt index 77f2fcc..8c954df 100644 --- a/doc/devices_classes_branch.txt +++ b/doc/devices_classes_branch.txt @@ -408,9 +408,17 @@ -------------------------------------------------------------------------------- TODO NEXT +DONE * provide a way that a device class instance can have a registered callback that passes the message that has just been handled, but without loosing - the reading update. + the reading update. The main purpose of this is for logging and notification + that new data has arrived (for event driven handling instead of polling) + + + + +* implement neater unknown message handler in the router + do it as a callback rather than overwriting the default method * design a way that a MIHO005 class instance can be auto generated from a product_id field diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index b6191ff..1a2588e 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -259,6 +259,7 @@ self.config = Config() class Capabilities(): pass self.capabilities = Capabilities() + self.updated_cb = None def has_switch(self): return hasattr(self.capabilities, "switch") @@ -281,14 +282,25 @@ pass def incoming_message(self, payload): - # incoming_message (OOK or OpenThings as appropriate, stripped of header? decrypted, decoded to pydict) - # default action of base class is to just print the payload - print("incoming(unhandled):%s" % payload) + """Entry point for a message to be processed""" + #This is the base-class entry point, don't override this, but override handle_message + self.handle_message(payload) + if self.updated_cb != None: + self.updated_cb(self, payload) + + def handle_message(self, payload): + """Default handling for a new message""" + print("incoming(unhandled): %s" % payload) def send_message(self, payload): print("send_message %s" % payload) # A raw device has no knowledge of how to send, the sub class provides that. + def when_updated(self, callback): + """Provide a callback handler to be called when a new message arrives""" + self.updated_cb = callback + # signature: update(self, message) + def __repr__(self): return "Device()" @@ -373,20 +385,8 @@ """Send a join-ack to the real device""" self.send_message("join ack") # TODO - def incoming_message(self, payload): - #TODO: interface with air_interface - """Handle incoming messages for this device""" - #NOTE: we must have already decoded the message with OpenThings to be able to get the addresses out - # so payload at this point must be a pydict? - - #we know at this point that it's a FSK message - #OpenThingsAirInterface has already decrypted and decoded - #so we get a pydict payload here with header and recs in it - #the header has the address which is used for routing - - #TODO join request might be handled generically here - #TODO: subclass can override and call back to this if it wants to - raise RuntimeError("Method unimplemented") #TODO + #def handle_message(self, payload): + #override for any specific handling def send_message(self, payload): #TODO: interface with air_interface @@ -465,8 +465,7 @@ def __repr__(self): return "MIHO005(%s)" % str(hex(self.device_id)) - - def incoming_message(self, payload): + def handle_message(self, payload): for rec in payload["recs"]: paramid = rec["paramid"] #TODO: consider making this table driven and allowing our base class to fill our readings in for us diff --git a/src/monitor_mihome.py b/src/monitor_mihome.py index 0e6ec76..9f811cb 100644 --- a/src/monitor_mihome.py +++ b/src/monitor_mihome.py @@ -6,7 +6,7 @@ import Logger import time -MY_SENSOR_ID = 0x68b # manually captured from a previous run +MY_SENSOR_ID = 0x68b # manually captured from a previous run DUMMY_SENSOR_ID = 0x111 # for testing unknown messages @@ -17,20 +17,22 @@ print("starting monitor tester") energenie.init() + #TESTING # Manually seed the device registry and router with a known device address purple = energenie.Devices.MIHO005(MY_SENSOR_ID) energenie.registry.add(purple, "purple") energenie.fsk_router.add((energenie.Devices.MFRID_ENERGENIE, energenie.Devices.PRODUCTID_MIHO005, MY_SENSOR_ID), purple) def new_data(self, message): - print("new data for %s\n%s\n\n" % (self, message)) + print("new data for %s" % self) + message.dump() Logger.logMessage(message) - #TODO: Provide a notify callback for when our device gets new data - ##purple.when_incoming(new_data) + purple.when_updated(new_data) # Override the default unknown handler, so we can show data from unregistered devices def unk(address, message): - print("Unknown device:%s\n%s\n\n" % (address, message)) + print("Unknown device:%s" % str(hex(address[2]))) + message.dump() #TODO: add device to registry and to fsk_router table #note: requires auto class create from product_id to be working first Logger.logMessage(message) @@ -40,28 +42,27 @@ try: while True: - # Send a synthetic message to the device + #TESTING: build a synthetic message msg = energenie.OpenThings.Message(energenie.Devices.MIHO005_REPORT) msg[energenie.OpenThings.PARAM_VOLTAGE]["value"] = 240 - # Poke the synthetic unknown reading into the router and let it route it to the device class instance - msg.set(header_sensorid=MY_SENSOR_ID) + #TESTING: Poke synthetic unknown into the router and let it route to unknown handler + msg.set(header_sensorid=DUMMY_SENSOR_ID) energenie.fsk_router.handle_message( (energenie.Devices.MFRID_ENERGENIE, energenie.Devices.PRODUCTID_MIHO005, DUMMY_SENSOR_ID), msg) - # Poke the synthetic known reading into the router and let it route it to the device class instance - msg.set(header_sensorid=DUMMY_SENSOR_ID) + #TESTING: Poke synthetic known into the router and let it route to our class instance + msg.set(header_sensorid=MY_SENSOR_ID) energenie.fsk_router.handle_message( (energenie.Devices.MFRID_ENERGENIE, energenie.Devices.PRODUCTID_MIHO005, MY_SENSOR_ID), msg) + #TODO: Knit with real radio # Process any received messages from the real radio ##energenie.loop() - print("purple voltage:%s" % purple.get_voltage()) + print("voltage:%s" % purple.get_voltage()) time.sleep(1) - - finally: energenie.finished()