diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index a0fc436..57255c4 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -109,6 +109,7 @@ #----- NEW DEVICE CLASSES ----------------------------------------------------- class Device(): + """A generic connected device abstraction""" def __init__(self, air_interface): self.air_interface = air_interface class Config(): pass @@ -140,18 +141,14 @@ print("incoming:%s" % payload) def send_message(self, payload): - if self.air_interface != None: - #TODO: might want to send the config, either as a send parameter, - #or by calling air_interface.configure() first? - self.air_interface.send(payload) - else: - m = self.manufacturer_id - p = self.product_id - d = self.device_id - print("send_message(mock[%s %s %s]):%s" % (str(m), str(p), str(d), payload)) + print("send_message %s" % payload) + + def __repr__(self): + return "Device()" class EnergenieDevice(Device): + """An abstraction for any kind of Energenie connected device""" def __init__(self, air_interface, device_id=None): Device.__init__(self, air_interface) self.device_id = device_id @@ -159,16 +156,34 @@ def get_device_id(self): # -> id:int return self.device_id + def __repr__(self): + return "Device(%s)" % str(self.device_id) + class LegacyDevice(EnergenieDevice): + """An abstraction for Energenie green button legacy OOK devices""" def __init__(self, air_interface): EnergenieDevice.__init__(self, air_interface) self.config.frequency = 433.92 self.config.modulation = "OOK" self.config.codec = "4bit" + def __repr__(self): + return "LegacyDevice(%s)" % str(self.device_id) + + def send_message(self, payload): + if self.air_interface != None: + #TODO: might want to send the config, either as a send parameter, + #or by calling air_interface.configure() first? + self.air_interface.send(payload) + else: + d = self.device_id #TODO: Not part of Device now + print("send_message(mock[%s]):%s" % (str(d), payload)) + + class MiHomeDevice(EnergenieDevice): + """An abstraction for Energenie new style MiHome FSK devices""" def __init__(self, air_interface, device_id=None): EnergenieDevice.__init__(self, air_interface, device_id) self.config.frequency = 433.92 @@ -182,14 +197,30 @@ #self.config.encryptPID = CRYPT_PID #self.config.encryptPIP = CRYPT_PIP + def __repr__(self): + return "MiHomeDevice(%s,%s,%s)" % (str(self.manufacturer_id), str(self.product_id), str(self.device_id)) + def get_manufacturer_id(self): # -> id:int return self.manufacturer_id def get_product_id(self): # -> id:int return self.product_id + def send_message(self, payload): + if self.air_interface != None: + #TODO: might want to send the config, either as a send parameter, + #or by calling air_interface.configure() first? + self.air_interface.send(payload) + else: + m = self.manufacturer_id #TODO: Not part of Device now + p = self.product_id #TODO: Not part of Device now + d = self.device_id #TODO: Not part of Device now + print("send_message(mock[%s %s %s]):%s" % (str(m), str(p), str(d), payload)) -class ENER002(LegacyDevice): # Green button switch + + +class ENER002(LegacyDevice): + """A green-button switch""" def __init__(self, air_interface=None, device_id=None): LegacyDevice.__init__(self, air_interface) #NOTE: tuple of (house_address, device_index) @@ -212,7 +243,8 @@ self.send_message("turn off") # TODO -class MIHO005(MiHomeDevice): # Adaptor Plus +class MIHO005(MiHomeDevice): + """An Energenie MiHome Adaptor Plus""" def __init__(self, air_interface=None, device_id=None): MiHomeDevice.__init__(self, air_interface) self.product_id = PRODUCTID_MIHO005 @@ -294,7 +326,8 @@ return self.readings.real_power -class MIHO006(MiHomeDevice): # Home Monitor +class MIHO006(MiHomeDevice): + """An Energenie MiHome Home Monitor""" def __init__(self, air_interface=None, device_id=None): MiHomeDevice.__init__(self, air_interface) self.product_id = PRODUCTID_MIHO006 @@ -314,7 +347,8 @@ return self.readings.current -class MIHO013(MiHomeDevice): # eTRV +class MIHO013(MiHomeDevice): + """An Energenie MiHome eTRV Radiator Valve""" def __init__(self, air_interface=None, device_id=None): MiHomeDevice.__init__(self, air_interface) self.product_id = PRODUCTID_MIHO013 diff --git a/src/energenie/Registry.py b/src/energenie/Registry.py index effd849..621abce 100644 --- a/src/energenie/Registry.py +++ b/src/energenie/Registry.py @@ -86,13 +86,43 @@ # TODO: serialisation format for the individual device meta record? json? -class DeviceRegistry(): - pass +def log_method(m): + def inner(*args, **kwargs): + print("CALL %s with: %s %s" % (m, args, kwargs)) + r = m(*args, **kwargs) + print("RETURN %s with: %s" % (m, r)) + return r + return inner - @unimplemented + +class RegistryStore(): + """A mock in-memory only store, for testing and debugging""" def __init__(self, filename): - pass - # bind this object to a persisted file registry + self.filename = filename #TODO: Intentionally not used elsewhere + self.store = {} + + #@log_method + def __setitem__(self, key, value): + self.store[key] = value + + #@log_method + def __getitem__(self, key): + return self.store[key] + + #@log_method + def __delitem__(self, key): + del self.store[key] + + #@log_method + def keys(self): + return self.store.keys() + + + +class DeviceRegistry(): + """A persistent registry for device class instance configurations""" + def __init__(self, filename): + self.store = RegistryStore(filename) # A dummy store, for testing @unimplemented def load(self): @@ -104,35 +134,41 @@ pass # persist registry to disk/write back new entries - @unimplemented def add(self, device, name): - pass - # add a device class instance to the registry with a friendly name + """Add a device class instance to the registry, with a friendly name""" + self.store[name] = device - @unimplemented def get(self, name): # -> Device - pass - # get a device by name from the registry - # create a new device class instance from a name + """Get the description for a device class from the store, and construct a class instance""" + c = self.store[name] + #TODO: Construct a new device class that is configured as per this data + #for now, just return the metadata, it could be serialised data too + return c - @unimplemented def delete(self, name): - pass - # delete a device from the registry + """Delete the named class instance""" + del self.store[name] - @unimplemented def auto_create(self, context): - pass - # auto-create variables in a given scope, for all persisted registry entries + """auto-create variables in the provided context, for all persisted registry entries""" + if context == None: + raise ValueError("Must provide a context to hold new variables") - @unimplemented + for name in self.store.keys(): + c = self.get(name) #TODO: should return an instantiated class + # This creates a variable inside the context of this name, points to class instance + setattr(context, name, c) + def list(self): - pass - # list the registry in some printable format (like a configuration record) + """List the registry in a vaguely printable format, mostly for debug""" + for k in self.store.keys(): + print("DEVICE %s" % k) + print(" %s" % self.store[k]) registry = DeviceRegistry("registry.txt") + # This will create all class instance variables in the module that imports the registry. # So, if there is an entry called "tv" in the registry, then the app module # will get a variable called tv that is bound to the appropriate device instance. @@ -140,20 +176,28 @@ # as it has switching capability. # # usage: +# import sys # from Registry import registry -# registry.auto_create(modules[__file__]) +# registry.auto_create(sys.modules[__file__]) +#----- SIMPLE TEST HARNESS ---------------------------------------------------- -# With the registry, these would be added, so that they could be auto restored -# on next boot -# Registry.add(tv) -# Registry.add(fan) +if __name__ == "__main__": -# Note, when adding registry, all of this data will be stored in the persisted -# registry, just start the registry and it creates all your object variables -# for you from it's metadata. -# Registry.start(some_context) -# where some_context is the scope that the variables tv and fan are created in. + # seed the registry + registry.add(Devices.MIHO005(device_id=0x68b), "tv") + registry.add(Devices.ENER002(device_id=(0xC8C8C, 1)), "fan") + + # test the auto create mechanism + import sys + registry.auto_create(sys.modules[__name__]) + + # variables should now be created in module scope + print(tv) + print(fan) + + tv.turn_on() + fan.turn_on() # END