diff --git a/doc/classes.txt b/doc/classes.txt index 0fca1fe..b9a77e4 100644 --- a/doc/classes.txt +++ b/doc/classes.txt @@ -253,6 +253,71 @@ -------------------------------------------------------------------------------- +DESIGN NOTES - registry data store + +REQUIREMENT: I want a simple persistent kvp database with the following features: + +1. A file format that is portable across all platforms + + so that a single registry file could be copied from a tutorial onto + any machine and it would just work + +2. A file format that is human readable and easily editable + + so that users could create or edit the file just like a config file + +3. A simple read and write key/value abstraction in python + with a full CRUD lifecycle + + so that new kvp's can be created, read, updated and deleted. + +4. Doesn't have to be hugely efficient or store very large data sets + + it's mostly used for configuration data that rarely changes, + or last known values that tend to be quite small. + +5. MIT licence + + so that it can be just embedded in an existing project + +6. A single python file + + so it is easy to embed + +7. Works out of the box with no changes on Python 2 and Python 3 + + so it doesn't have to be configured or changed and does not limit + or dictate a specific python version. + +Additionally, it might: + +8. A option to add multi process locking later if required, but not + included by default + + so that it could be used as a simple central database for multiple + programs sharing the same data set. + +9. understand read only and read/write intents better + + when using configuration data and last known values, it is useful + to keep them in the same single file, so it is easy to copy + to other machines. Some data is naturally 'write once' and + very configuration based. Some data is naturally 'write often'. + It might be nice if these two types of data could appear in the same + file, but the locking/performance and resilience issues be handled + differently for the two classes of data - e.g. perhaps having + two connections to the same database file, one in read only mode + for config records, and one in read/write mode for last use data. + There might also be different namespace prefixes in the file + so that the key sets are separate, or there may be a way to + link them so that when you read a record you get both the static + config data and the fast changing last use data as a single + record. But this then implies when you do an update, you + probably want to update part of a record rather than the + whole record. + + +-------------------------------------------------------------------------------- DESIGN NOTES - discovery process a way to sequence transmit messages to allow legacy devices to learn a code. diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index 3ed6a59..a0fc436 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -113,9 +113,6 @@ self.air_interface = air_interface class Config(): pass self.config = Config() - self.manufacturer_id = None - self.product_id = None - self.device_id = None def has_switch(self): return False @@ -129,35 +126,13 @@ def get_radio_config(self): return self.config - - #TODO: no manufacturerid, productid, deviceid for legacy devices? - # so does this move into MiHomeDevice? - - def get_manufacturer_id(self): # -> id:int - return self.manufacturer_id - - def get_product_id(self): # -> id:int - return self.product_id - - def get_device_id(self): # -> id:int - return self.device_id - - def get_last_receive_time(self): # ->timestamp + """The timestamp of the last time any message was received by this device""" return self.last_receive_time - def get_last_send_time(self): # -> timestamp - return self.last_send_time - def get_next_receive_time(self): # -> timestamp + """An estimate of the next time we expect a message from this device""" pass - #TODO this should probably be calculated - #TODO not sure yet if this lives here - - def get_next_send_time(self): # -> timestamp - pass - #TODO this should probably be calculated - #TODO not sure yet if this lives here def incoming_message(self, payload): # incoming_message (OOK or OpenThings as appropriate, stripped of header? decrypted, decoded to pydict) @@ -166,6 +141,8 @@ 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 @@ -175,9 +152,12 @@ class EnergenieDevice(Device): - def __init__(self, air_interface): + def __init__(self, air_interface, device_id=None): Device.__init__(self, air_interface) - self.manufacturer_id = MFRID_ENERGENIE + self.device_id = device_id + + def get_device_id(self): # -> id:int + return self.device_id class LegacyDevice(EnergenieDevice): @@ -189,18 +169,27 @@ class MiHomeDevice(EnergenieDevice): - def __init__(self, air_interface): - EnergenieDevice.__init__(self, air_interface) + def __init__(self, air_interface, device_id=None): + EnergenieDevice.__init__(self, air_interface, device_id) self.config.frequency = 433.92 self.config.modulation = "FSK" self.config.codec = "OpenThings" + self.manufacturer_id = MFRID_ENERGENIE + self.product_id = None + #Different devices might have different PIP's #if we are cycling codes on each message? #self.config.encryptPID = CRYPT_PID #self.config.encryptPIP = CRYPT_PIP + def get_manufacturer_id(self): # -> id:int + return self.manufacturer_id -class ENER002(LegacyDevice): + def get_product_id(self): # -> id:int + return self.product_id + + +class ENER002(LegacyDevice): # Green button switch def __init__(self, air_interface=None, device_id=None): LegacyDevice.__init__(self, air_interface) #NOTE: tuple of (house_address, device_index) diff --git a/src/energenie/radio.py b/src/energenie/radio.py index 7b04b83..2d3c214 100644 --- a/src/energenie/radio.py +++ b/src/energenie/radio.py @@ -232,28 +232,29 @@ return rxlist # Python len(rxlist) tells us how many bytes including length byte if present -@untested -def receive_len(size): - """Receive a fixed payload size""" - - bufsize = size - - Buffer = ctypes.c_ubyte * bufsize - rxbuf = Buffer() - buflen = ctypes.c_ubyte(bufsize) - #RADIO_RESULT radio_get_payload_len(uint8_t* buf, uint8_t buflen) - - result = radio_get_payload_len_fn(rxbuf, buflen) - - if result != 0: # RADIO_RESULT_OK - raise RuntimeError("Receive failed, error code %s" % hex(result)) - - # turn buffer into a list of bytes, using 'size' as the counter - rxlist = [] - for i in range(size): - rxlist.append(rxbuf[i]) - - return rxlist # Python len(rxlist) tells us how many bytes including length byte if present +#TODO: Placeholder for when we do OOK receive +#@untested +#def receive_len(size): +# """Receive a fixed payload size""" +# +# bufsize = size +# +# Buffer = ctypes.c_ubyte * bufsize +# rxbuf = Buffer() +# buflen = ctypes.c_ubyte(bufsize) +# #RADIO_RESULT radio_get_payload_len(uint8_t* buf, uint8_t buflen) +# +# result = radio_get_payload_len_fn(rxbuf, buflen) +# +# if result != 0: # RADIO_RESULT_OK +# raise RuntimeError("Receive failed, error code %s" % hex(result)) +# +# # turn buffer into a list of bytes, using 'size' as the counter +# rxlist = [] +# for i in range(size): +# rxlist.append(rxbuf[i]) +# +# return rxlist # Python len(rxlist) tells us how many bytes including length byte if present def standby():