Neater callback mechanism for device class instances to use.
This new architecture ensures default message processing always happens
1 parent 1c24608 commit a84398bbec7d8a50a9ad261178229cc4abf90b63
@David Whale David Whale authored on 25 May 2016
Showing 3 changed files
View
17
doc/devices_classes_branch.txt
 
--------------------------------------------------------------------------------
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
 
* provide an option in the router to turn on or off auto device registration
View
56
src/energenie/Devices.py
class Config(): pass
self.config = Config()
class Capabilities(): pass
self.capabilities = Capabilities()
self.updated_cb = None
 
def has_switch(self):
return hasattr(self.capabilities, "switch")
 
"""An estimate of the next time we expect a message from this device"""
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()"
 
def join_ack(self):
"""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
#is payload a pydict with header at this point, and we have to call OpenThings.encode?
 
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
# then just define the mapping table in __init__ (i.e. paramid->Readings field name)
View
30
src/monitor_mihome.py
import energenie
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
 
 
#----- TEST APPLICATION -------------------------------------------------------
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)
energenie.fsk_router.handle_unknown = unk
##energenie.fsk_router.when_unknown(unk)
 
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()