diff --git a/doc/devices_classes_branch.txt b/doc/devices_classes_branch.txt index f8e5b05..f354dba 100644 --- a/doc/devices_classes_branch.txt +++ b/doc/devices_classes_branch.txt @@ -223,16 +223,10 @@ -------------------------------------------------------------------------------- PLAN UP TO: MERGE BACK TO MASTER ----- PRE-RELEASE CLEAN-UP - -Move all global code from Registry.py (setup of router, registry) to __init__ +---- TEST HARNESS IMPROVEMENT Enable all tests and see if they work as a group - -Remove all commented out code - -Search TODO:s and remove any that are done - +(Registry changes should now allow multiple tests to run together) ---- TEST ON REAL HARDWARE diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index d795b99..563096e 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -15,47 +15,47 @@ MFRID_ENERGENIE = 0x04 MFRID = MFRID_ENERGENIE -#PRODUCTID_MIHO001 = # Home Hub -#PRODUCTID_MIHO002 = # Control only (Uses Legacy OOK protocol) -#PRODUCTID_MIHO003 = 0x0? # Hand Controller +##PRODUCTID_MIHO001 = # Home Hub +##PRODUCTID_MIHO002 = # Control only (Uses Legacy OOK protocol) +##PRODUCTID_MIHO003 = 0x0? # Hand Controller PRODUCTID_MIHO004 = 0x01 # Monitor only PRODUCTID_MIHO005 = 0x02 # Adaptor Plus PRODUCTID_MIHO006 = 0x05 # House Monitor -#PRODUCTID_MIHO007 = 0x0? # Double Wall Socket White -#PRODUCTID_MIHO008 = 0x0? # Single light switch -#PRODUCTID_MIHO009 not used -#PRODUCTID_MIHO010 not used -#PRODUCTID_MIHO011 not used -#PRODUCTID_MIHO012 not used +##PRODUCTID_MIHO007 = 0x0? # Double Wall Socket White +##PRODUCTID_MIHO008 = 0x0? # Single light switch +##PRODUCTID_MIHO009 not used +##PRODUCTID_MIHO010 not used +##PRODUCTID_MIHO011 not used +##PRODUCTID_MIHO012 not used PRODUCTID_MIHO013 = 0x03 # eTRV -#PRODUCTID_MIHO014 = 0x0? # In-line Relay -#PRODUCTID_MIHO015 not used -#PRODUCTID_MIHO016 not used -#PRODUCTID_MIHO017 -#PRODUCTID_MIHO018 -#PRODUCTID_MIHO019 -#PRODUCTID_MIHO020 -#PRODUCTID_MIHO021 = 0x0? # Double Wall Socket Nickel -#PRODUCTID_MIHO022 = 0x0? # Double Wall Socket Chrome -#PRODUCTID_MIHO023 = 0x0? # Double Wall Socket Brushed Steel -#PRODUCTID_MIHO024 = 0x0? # Style Light Nickel -#PRODUCTID_MIHO025 = 0x0? # Style Light Chrome -#PRODUCTID_MIHO026 = 0x0? # Style Light Steel -#PRODUCTID_MIHO027 starter pack bundle -#PRODUCTID_MIHO028 eco starter pack -#PRODUCTID_MIHO029 heating bundle -#PRODUCTID_MIHO030 not used -#PRODUCTID_MIHO031 not used -#PRODUCTID_MIHO032 not used -#PRODUCTID_MIHO033 not used -#PRODUCTID_MIHO034 not used -#PRODUCTID_MIHO035 not used -#PRODUCTID_MIHO036 not used -#PRODUCTID_MIHO037 Adaptor Plus Bundle -#PRODUCTID_MIHO038 2-gang socket Bundle -#PRODUCTID_MIHO039 2-gang socket Bundle black nickel -#PRODUCTID_MIHO040 2-gang socket Bundle chrome -#PRODUCTID_MIHO041 2-gang socket Bundle stainless steel +##PRODUCTID_MIHO014 = 0x0? # In-line Relay +##PRODUCTID_MIHO015 not used +##PRODUCTID_MIHO016 not used +##PRODUCTID_MIHO017 +##PRODUCTID_MIHO018 +##PRODUCTID_MIHO019 +##PRODUCTID_MIHO020 +##PRODUCTID_MIHO021 = 0x0? # Double Wall Socket Nickel +##PRODUCTID_MIHO022 = 0x0? # Double Wall Socket Chrome +##PRODUCTID_MIHO023 = 0x0? # Double Wall Socket Brushed Steel +##PRODUCTID_MIHO024 = 0x0? # Style Light Nickel +##PRODUCTID_MIHO025 = 0x0? # Style Light Chrome +##PRODUCTID_MIHO026 = 0x0? # Style Light Steel +##PRODUCTID_MIHO027 starter pack bundle +##PRODUCTID_MIHO028 eco starter pack +##PRODUCTID_MIHO029 heating bundle +##PRODUCTID_MIHO030 not used +##PRODUCTID_MIHO031 not used +##PRODUCTID_MIHO032 not used +##PRODUCTID_MIHO033 not used +##PRODUCTID_MIHO034 not used +##PRODUCTID_MIHO035 not used +##PRODUCTID_MIHO036 not used +##PRODUCTID_MIHO037 Adaptor Plus Bundle +##PRODUCTID_MIHO038 2-gang socket Bundle +##PRODUCTID_MIHO039 2-gang socket Bundle black nickel +##PRODUCTID_MIHO040 2-gang socket Bundle chrome +##PRODUCTID_MIHO041 2-gang socket Bundle stainless steel # Default keys for OpenThings encryption and decryption CRYPT_PID = 242 @@ -67,48 +67,8 @@ BROADCAST_ID = 0xFFFFFF # Energenie broadcast - -#TODO: Remove these -##@unimplemented # no longer supported -##def getDescription(mfrid, productid): -## if mfrid == MFRID_ENERGENIE: -## mfr = "Energenie" -## if productid == PRODUCTID_MIHO004: -## product = "MIHO004 MONITOR" -## elif productid == PRODUCTID_MIHO005: -## product = "MIHO005 ADAPTOR PLUS" -## elif productid == PRODUCTID_MIHO006: -## product = "MIHO006 HOUSE MONITOR" -## elif productid == PRODUCTID_MIHO013: -## product = "MIHO013 ETRV" -## else: -## product = "UNKNOWN_%s" % str(hex(productid)) -## else: -## mfr = "UNKNOWN_%s" % str(hex(mfrid)) -## product = "UNKNOWN_%s" % str(hex(productid)) -#### -## return "Manufacturer:%s Product:%s" % (mfr, product) -#### -## -###TODO this might be deprecated now, and replaced with the Device classes. -###e.g. if there is a turn_on method or get_switch method, it has a switch. -###still used in switch.py demo (will be until device classes deployed into tests) -#### -##@unimplemented # no longer supported -##def hasSwitch(mfrid, productid): -## if mfrid != MFRID: return False -## if productid == PRODUCTID_MIHO005: return True -## return False - - #----- DEFINED MESSAGE TEMPLATES ---------------------------------------------- -##TODO: remove -##import copy -## -##def create_message(message): -## return copy.deepcopy(message) - SWITCH = { "header": { @@ -232,19 +192,6 @@ ] } -#TODO: remove -##def send_join_ack(radio, mfrid, productid, sensorid): -## # send back a JOIN ACK, so that join light stops flashing -## response = OpenThings.Message(JOIN_ACK) -## response.set(header_mfrid=mfrid, -## header_productid=productid, -## header_sensorid=sensorid) -## p = OpenThings.encode(response) -## radio.transmitter() -## radio.transmit(p, inner_times=2) -## radio.receiver() - - #----- CONTRACT WITH AIR-INTERFACE -------------------------------------------- @@ -253,8 +200,8 @@ # # synchronous send # synchronous receive -# TODO: asynchronous send (deferred) - implies a callback on 'done, fail, timeout' -# TODO: asynchronous receive (deferred) - implies a callback on 'done, fail, timeout' +#TODO: asynchronous send (deferred) - implies a callback on 'done, fail, timeout' +#TODO: asynchronous receive (deferred) - implies a callback on 'done, fail, timeout' # air_interface has: # configure(parameters) @@ -457,10 +404,10 @@ def send_message(self, payload): #TODO: interface with air_interface # Encode the payload two bits per byte as per OOK spec - #TODO, should we just pass a payload (as a pydict or tuple) to the air_interface adaptor + #TODO: should we just pass a payload (as a pydict or tuple) to the air_interface adaptor #and let it encode it, to be consistent with the FSK MiHome devices? #payload could be a 3-tuple of (house_address, device_address, state) - #bytes = TwoBit.build_switch_msg(payload, house_address=self.device_id[0], device_address=self.device_id[1]) + ##bytes = TwoBit.build_switch_msg(payload, house_address=self.device_id[0], device_address=self.device_id[1]) if self.air_interface != None: #TODO: might want to send the config, either as a send parameter, @@ -486,8 +433,8 @@ #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 + ##self.config.encryptPID = CRYPT_PID + ##self.config.encryptPIP = CRYPT_PIP def get_config(self): """Get the persistable config, enough to reconstruct this class from a factory""" @@ -522,7 +469,7 @@ msg[OpenThings.PARAM_JOIN] = {"wr":False, "typeid":OpenThings.Value.UINT, "length":0} self.send_message(msg) - #def handle_message(self, payload): + ##def handle_message(self, payload): #override for any specific handling def send_message(self, payload): @@ -558,7 +505,7 @@ def turn_on(self): - #TODO should this be here, or in LegacyDevice?? + #TODO: should this be here, or in LegacyDevice?? #addressing should probably be in LegacyDevice #child devices might interpret the command differently payload = { @@ -771,13 +718,13 @@ return self.readings.setpoint_temperature def set_setpoint_temperature(self, temperature): - self.send_message("set setpoint temp") # TODO command + self.send_message("set setpoint temp") #TODO: command def get_valve_position(self): # -> position:int? - pass # TODO is this possible? + pass #TODO: is this possible? def set_valve_position(self, position): - pass # TODO command, is this possible? + pass #TODO: command, is this possible? self.send_message("set valve pos") #TODO #TODO: difference between 'is on and 'is requested on' @@ -786,18 +733,18 @@ #TODO: switch state might be 'turning_on' or 'turning_off' if send request and not heard response yet def turn_on(self): # command - pass # TODO command i.e. valve position? + pass #TODO: command i.e. valve position? self.send_message("turn on") #TODO def turn_off(self): # command - pass # TODO command i.e. valve position? + pass #TODO: command i.e. valve position? self.send_message("turn off") #TODO def is_on(self): # query last known reported state (unknown if changing?) - pass # TODO i.e valve is not completely closed? + pass #TODO: i.e valve is not completely closed? def is_off(self): # query last known reported state (unknown if changing?) - pass # TODO i.e. valve is completely closed? + pass #TODO: i.e. valve is completely closed? #----- DEVICE FACTORY --------------------------------------------------------- @@ -866,5 +813,6 @@ print(i) return i + # END diff --git a/src/energenie/KVS.py b/src/energenie/KVS.py index 003cb85..17a55ed 100644 --- a/src/energenie/KVS.py +++ b/src/energenie/KVS.py @@ -2,7 +2,7 @@ # # A generic key value store -from lifecycle import * +##from lifecycle import * class NotPersistableError(Exception): pass @@ -171,7 +171,7 @@ for key in self.store: obj = self.store[key] - # TODO: for this to work, we need to call the inner object get_config() to get a persistable version + #TODO: for this to work, we need to call the inner object get_config() to get a persistable version # that the user of this class can recreate it from later f.write("ADD %s\n" % key) @@ -184,5 +184,6 @@ self.filename = filename # Remember that we are linked to this file + # END diff --git a/src/energenie/KVS_test.py b/src/energenie/KVS_test.py index 1a3d730..d6261cd 100644 --- a/src/energenie/KVS_test.py +++ b/src/energenie/KVS_test.py @@ -189,7 +189,7 @@ kvs["tv1"] = TV(1) show_file(self.KVS_FILENAME) - kvs["tv1"] = TV(2) ####HERE### + kvs["tv1"] = TV(2) show_file(self.KVS_FILENAME) @test_1 diff --git a/src/energenie/OnAir.py b/src/energenie/OnAir.py index c173a8a..3b4b9a1 100644 --- a/src/energenie/OnAir.py +++ b/src/energenie/OnAir.py @@ -48,7 +48,7 @@ timeout = 1000 #ms self.rx_defaults = RxDefaults() - #@log_method + ##@log_method def send(self, payload, radio_params=None): # payload is a pydict suitable for OpenThings # radio_params is an overlay on top of radio tx defaults @@ -62,7 +62,7 @@ radio.transmit(p, outer_times=1, inner_times=4, outer_delay=0) # radio auto-returns to previous state after transmit completes - #@log_method + ##@log_method def receive(self, radio_params): # -> (radio_measurements, address or None, payload or None) # radio_params is an overlay on top of radio rx defaults (e.g. poll rate, timeout, min payload, max payload) # radio_measurements might include rssi reading, short payload report, etc @@ -77,7 +77,7 @@ radio.receiver(fsk=True) while True: # timer not expired if radio.is_receive_waiting(): - payload = radio.receive() # TODO payload, radio_measurements = radio.receive() + payload = radio.receive() #TODO: payload, radio_measurements = radio.receive() now = time.time() p = OpenThings.decode(payload, receive_timestamp=now) #TODO: if crc failure, report it, but keep trying @@ -87,11 +87,11 @@ #TODO: return radio to state it was before receiver (e.g. standby) - radio needs a pop() on this too? if payload == None: # nothing received in timeout - return (None, None, None) # (radio_measurements, address, payload) # TODO: might be measurements, average min max? + return (None, None, None) # (radio_measurements, address, payload) #TODO: might be measurements, average min max? #TODO: extract addresses: header_manufacturerid, header_productid header_deviceid -> (m, p, d) m, p, d = None, None, None - radio_measurements = None # TODO get from radio.receive() + radio_measurements = None #TODO: get from radio.receive() address = (m, p, d) return (radio_measurements, address, payload) @@ -116,7 +116,7 @@ timeout = 1000 #ms self.rx_defaults = RxDefaults() - #@log_method + ##@log_method def send(self, payload, radio_params=None): # payload is just a list of bytes, or a byte buffer # radio_params is an overlay on top of radio tx defaults @@ -134,7 +134,7 @@ radio.transmit(bytes, outer_times=1, inner_times=8, outer_delay=0) #TODO: radio params # radio auto-pops to state before transmit - #@log_method + ##@log_method def receive(self, radio_params): # -> (radio_measurements, address or None, payload or None) # radio_params is an overlay on top of radio rx defaults (e.g. poll rate, timeout, min payload, max payload) # radio_measurements might include rssi reading, short payload report, etc @@ -148,7 +148,7 @@ while True: # timer not expired if radio.is_receive_waiting(): #TODO: radio config should set receive preamble 4 bytes to prevent false triggers - payload = radio.receive(size=12) # TODO payload, radio_measurements = radio.receive() + payload = radio.receive(size=12) #TODO: payload, radio_measurements = radio.receive() p = TwoBit.decode(payload) #TODO: if failure, report it, but keep trying #if check passes... @@ -157,7 +157,7 @@ #TODO: return radio to state it was before receiver (e.g. standby) - radio needs a pop() on this too? if payload == None: # nothing received in timeout - return (None, None, None) # (radio_measurements, address, payload) # TODO: might be measurements, average min max? + return (None, None, None) # (radio_measurements, address, payload) #TODO: might be measurements, average min max? #TODO: extract addresses (house_address, device_index) radio_measurements = None #TODO: return this from radio.receive() diff --git a/src/energenie/OpenThings.py b/src/energenie/OpenThings.py index c12897f..04d0070 100644 --- a/src/energenie/OpenThings.py +++ b/src/energenie/OpenThings.py @@ -2,7 +2,7 @@ # # Implement OpenThings message encoding and decoding -from lifecycle import * +##from lifecycle import * import time try: @@ -157,7 +157,7 @@ #----- MESSAGE DECODER -------------------------------------------------------- -#TODO if silly lengths or silly types seen in decode, this might imply +#TODO: if silly lengths or silly types seen in decode, this might imply #we're trying to process an encrypted packet without decrypting it. #the code should be more robust to this (by checking the CRC) @@ -169,12 +169,12 @@ # CHECK LENGTH if length+1 != len(payload) or length < 10: raise OpenThingsException("bad payload length") - #return { - # "type": "BADLEN", - # "len_actual": len(payload), - # "len_expected": length, - # "payload": payload[1:] - #} + ##return { + ## "type": "BADLEN", + ## "len_actual": len(payload), + ## "len_expected": length, + ## "payload": payload[1:] + ##} # DECODE HEADER mfrId = payload[1] @@ -192,7 +192,7 @@ # [0]len,mfrid,productid,pipH,pipL,[5] crypto.init(crypt_pid, encryptPIP) crypto.cryptPayload(payload, 5, len(payload)-5) # including CRC - #printhex(payload) + ##printhex(payload) # sensorId is in encrypted region sensorId = (payload[5]<<16) + (payload[6]<<8) + payload[7] header["sensorid"] = sensorId @@ -201,16 +201,16 @@ # CHECK CRC crc_actual = (payload[-2]<<8) + payload[-1] crc_expected = calcCRC(payload, 5, len(payload)-(5+2)) - #trace("crc actual:%s, expected:%s" %(hex(crc_actual), hex(crc_expected))) + ##trace("crc actual:%s, expected:%s" %(hex(crc_actual), hex(crc_expected))) if crc_actual != crc_expected: raise OpenThingsException("bad CRC") - #return { - # "type": "BADCRC", - # "crc_actual": crc_actual, - # "crc_expected": crc_expected, - # "payload": payload[1:], - #} + ##return { + ## "type": "BADCRC", + ## "crc_actual": crc_actual, + ## "crc_expected": crc_expected, + ## "payload": payload[1:], + ##} # DECODE RECORDS @@ -388,13 +388,13 @@ mask = 1<<(maxbits-1) bitno = maxbits-1 while mask != 0: - #trace("compare %s with %s" %(hex(value), hex(mask))) + ##trace("compare %s with %s" %(hex(value), hex(mask))) if (value & mask) == 0: - #trace("zero at bit %d" % bitno) + ##trace("zero at bit %d" % bitno) return bitno mask >>= 1 bitno-=1 - #trace("not found") + ##trace("not found") return None # NOT FOUND @@ -406,25 +406,25 @@ if value == -1: # always 0xFF, so always needs exactly 2 bits to represent (sign and value) return 2 # bits required - #trace("valuebits of:%d" % value) + ##trace("valuebits of:%d" % value) # Turn into a 2's complement representation MAXBYTES=15 MAXBITS = 1<<(MAXBYTES*8) - #TODO check for truncation? + #TODO: check for truncation? value = value & MAXBITS-1 - #trace("hex:%s" % hex(value)) + ##trace("hex:%s" % hex(value)) highz = Value.highestClearBit(value, MAXBYTES*8) - #trace("highz at bit:%d" % highz) + ##trace("highz at bit:%d" % highz) # allow for a sign bit, and bit numbering from zero neededbits = highz+2 - #trace("needed bits:%d" % neededbits) + ##trace("needed bits:%d" % neededbits) return neededbits @staticmethod def encode(value, typeid, length=None): - #trace("encoding:" + str(value)) + ##trace("encoding:" + str(value)) if typeid == Value.CHAR: if type(value) != str: value = str(value) @@ -490,13 +490,13 @@ bits = Value.valuebits(value) else: bits = Value.typebits(typeid) - #trace("need bits:" + str(bits)) + ##trace("need bits:" + str(bits)) # NORMALISE BITS TO BYTES #round up to nearest number of 8 bits # if already 8, leave 1,2,3,4,5,6,7,8 = 8 0,1,2,3,4,5,6,7 (((b-1)/8)+1)*8 # 9,10,11,12,13,14,15,16=16 bits = (((bits-1)/8)+1)*8 # snap to nearest byte boundary - #trace("snap bits to 8:" + str(bits)) + ##trace("snap bits to 8:" + str(bits)) value &= ((2**bits)-1) neg = True @@ -573,22 +573,6 @@ #----- CRC CALCULATION -------------------------------------------------------- -#int16_t crc(uint8_t const mes[], unsigned char siz) -#{ -# uint16_t rem = 0; -# unsigned char byte, bit; -# -# for (byte = 0; byte < siz; ++byte) -# { -# rem ^= (mes[byte] << 8); -# for (bit = 8; bit > 0; --bit) -# { -# rem = ((rem & (1 << 15)) ? ((rem << 1) ^ 0x1021) : (rem << 1)); -# } -# } -# return rem; -#} - def calcCRC(payload, start, length): rem = 0 for b in payload[start:start+length]: @@ -883,96 +867,4 @@ print("%s %s %s %s = %s" % (write, paramid, paramname, paramunit, str(value))) -#TODO: Remove this when the new Message() integration is tested -#@deprecated -#def showMessage(msg, timestamp=None): -# """Show the message in a friendly format""" -# -# # HEADER -# header = msg["header"] -# mfrid = header["mfrid"] -# productid = header["productid"] -# sensorid = header["sensorid"] -# if timestamp != None: -# print("receive-time:%s" % time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp))) -# print("mfrid:%s prodid:%s sensorid:%s" % (hex(mfrid), hex(productid), hex(sensorid))) -# -# # RECORDS -# for rec in msg["recs"]: -# wr = rec["wr"] -# if wr == True: -# write = "write" -# else: -# write = "read " -# -# try: -# paramname = rec["paramname"] # This only come out from decoded messages -# except: -# paramname = "" -## -# try: -# paramid = rec["paramid"] #This is only present on a input message (e.g SWITCH) -# paramname = paramid_to_paramname(paramid) -# paramid = str(hex(paramid)) -## except: -# paramid = "" -# -# try: -# paramunit = rec["paramunit"] # This only come out from decoded messages -# except: -# paramunit = "" -# -# if "value" in rec: -# value = rec["value"] -# else: -# value = None -# -# print("%s %s %s %s = %s" % (write, paramid, paramname, paramunit, str(value))) - - -# Example paths into message -# header_sensorid msg["header"]["sensorid"] -# recs_0_value msg["recs"][0]["value"] -# recs_WATER_DETECTOR_value msg["recs"].find_param("WATER_DETECTOR")["value"] - -#TODO: Remove this when the new Message() integration is tested -#@deprecated -#def alterMessage(message, **kwargs): -# """Change parameters in-place in a message template""" -# -# for arg in kwargs: -# path = arg.split("_") -# value = kwargs[arg] -# m = message -# -# for pkey in path[:-1]: -# try: -# # If it is convertable to an int, it's an array index -# pkey = int(pkey) -# except: -# # It must be a field name -# pass -# m = m[pkey] -# ##trace("old value:%s" % m[path[-1]]) -# m[path[-1]] = value -# -# ##trace("modified:" + str(message)) -# -# return message - -#TODO: Remove this when the new Message() integration is tested -#@deprecated -#def getFromMessage(message, keypath): -# """Get a field from a message, given an underscored keypath to the item""" -# path = keypath.split("_") -# -# for pkey in path[:-1]: -# try: -# pkey = int(pkey) -# except: -# pass -# message = message[pkey] -# return message[path[-1]] - - # END diff --git a/src/energenie/OpenThings_test.py b/src/energenie/OpenThings_test.py index 72ae5bd..7bfba56 100644 --- a/src/energenie/OpenThings_test.py +++ b/src/energenie/OpenThings_test.py @@ -296,35 +296,35 @@ @test_0 def test_attr_header(self): #9 msg["header"] msg["recs"][0] """READ(attr) the header""" - ## access a specific keyed entry like a normal pydict, for read + # access a specific keyed entry like a normal pydict, for read msg = Message(Devices.MIHO005_REPORT) print(msg["header"]) @test_0 def test_attr_header_field(self): #9 msg["header"] msg["recs"][0] """READ(attr) a field within the header""" - ## access a specific keyed entry like a normal pydict, for read + # access a specific keyed entry like a normal pydict, for read msg = Message(Devices.MIHO005_REPORT) print(msg["header"]["mfrid"]) @test_0 def test_attr_recs(self): #9 msg["header"] msg["recs"][0] """READ(attr) all recs""" - ## access a specific keyed entry like a normal pydict, for read + # access a specific keyed entry like a normal pydict, for read msg = Message(Devices.MIHO005_REPORT) print(msg["recs"]) @test_0 def test_attr_rec(self): #9 msg["header"] msg["recs"][0] """READ(attr) a single reg""" - ## access a specific keyed entry like a normal pydict, for read + # access a specific keyed entry like a normal pydict, for read msg = Message(Devices.MIHO005_REPORT) print(msg["recs"][0]) @test_0 def test_attr_rec_field(self): #9 msg["header"] msg["recs"][0] """READ(attr) a field in a rec""" - ## access a specific keyed entry like a normal pydict, for read + # access a specific keyed entry like a normal pydict, for read msg = Message(Devices.MIHO005_REPORT) print(msg["recs"][0]["value"]) @@ -413,7 +413,7 @@ def test_paramid_write_rec_overwrite(self): """WRITE (overwrite) a whole paramid rec""" msg = Message(recs_0={"paramid":PARAM_SWITCH_STATE, "wr":True, "value":33}) - #print(msg) + ##print(msg) msg[PARAM_SWITCH_STATE] = {"wr":True, "value":99} ##print(msg) print(msg[PARAM_SWITCH_STATE]) @@ -423,7 +423,7 @@ """WRITE (add) a whole paramid rec""" msg = Message(Devices.MIHO005_REPORT) msg[PARAM_AIR_PRESSURE] = {"wr":True, "value":1} - #print(msg) + ##print(msg) print(msg[PARAM_AIR_PRESSURE]) @test_0 @@ -474,7 +474,7 @@ unittest.main() if __name__ == "__main__": - ##TODO: Change these into unittest test cases + #TODO: Change these into unittest test cases ##test_value_encoder() ##test_value_decoder() ##test_payload_unencrypted() diff --git a/src/energenie/Registry.py b/src/energenie/Registry.py index f82b08d..ff2c537 100644 --- a/src/energenie/Registry.py +++ b/src/energenie/Registry.py @@ -4,9 +4,8 @@ # # NOTE: This is an initial, non persisted implementation only -from lifecycle import * +##from lifecycle import * -import time try: # Python 2 import Devices @@ -19,55 +18,6 @@ from KVS import KVS -#TODO: REMOVE -##directory = {} - -##@unimplemented # no longer supported -##def allkeys(d): -## result = "" -## for k in d: -## if len(result) != 0: -## result += ',' -## result += str(k) -## return result - - -## @unimplemented # no longer supported -## def update(message): -## """Update the local directory with information about this device""" -## now = time.time() -## header = message["header"] -## sensorId = header["sensorid"] -## ## -## if not (sensorId in directory): -## # new device discovered -## desc = Devices.getDescription(header["mfrid"], header["productid"]) -## print("ADD device:%s %s" % (hex(sensorId), desc)) -## directory[sensorId] = {"header": message["header"]} -## #trace(allkeys(directory)) -## ## -## directory[sensorId]["time"] = now -## #TODO would be good to keep recs, but need to iterate through all and key by paramid, -## #not as a list index, else merging will be hard. -## ## -## -## @unimplemented # no longer supported -## def size(): -## return len(directory) -## ## -## -## @unimplemented # no longer supported -## def get_sensorids(): -## return directory.keys() -## ## -## -## @unimplemented # no longer supported -## def get_info(sensor_id): -## return directory[sensor_id] - - - - #----- NEW DEVICE REGISTRY ---------------------------------------------------- # Done as a class, so we can have multiple registries if we want. @@ -168,27 +118,6 @@ i += 1 - -#TODO: Might move this to energenie.init() so that it is optional -#will make it possible to run all the test cases together also. -##registry = DeviceRegistry() -##import os -##if os.path.isfile(DeviceRegistry.DEFAULT_FILENAME): -## registry.load_from(DeviceRegistry.DEFAULT_FILENAME) - - -# 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. -# You can then just say tv.turn_on() regardless of the type of device it is, as long -# as it has switching capability. -# -# usage: -# import sys -# from Registry import registry -# registry.auto_create(sys.modules[__file__]) - - #----- DISCOVERY AND LEARNING ------------------------------------------------- #5. LEARN/DISCOVER: To be able to instigate and manage learn mode from within an app # @@ -396,68 +325,4 @@ self.reject_device(address, message) -# Might rename these, especially when we add in other protocols -# such as devices that are 868 wirefree doorbells etc. - -#TODO: Might move this to energenie.init() so that it is optional -#will make it possible to run all the test cases together also. - -#TODO: Name is not completely representative of function. -# This is the Energenie 433.92MHz with OpenThings -##fsk_router = Router("fsk") - -#OOK receive not yet written -#It will be used to be able to learn codes from Energenie legacy hand remotes -##ook_router = Router("ook") - - -## #TODO: Improve this interface -## # (temporary) helpful methods to switch between different discovery methods -## # Note that the __init__ automaticall registers itself with router -## ## -## def discovery_none(): -## fsk_router.when_unknown(None) -## ## -## def discovery_auto(): -## d = AutoDiscovery(registry, fsk_router) -## ##print("Using auto discovery") -## ## -## def discovery_ask(ask_fn): -## d = ConfirmedDiscovery(registry, fsk_router, ask_fn) -## ##print("using confirmed discovery") -## ## -## def discovery_autojoin(): -## d = JoinAutoDiscovery(registry, fsk_router) -## ##print("using auto join discovery") -## ## -## def discovery_askjoin(ask_fn): -## d = JoinConfirmedDiscovery(registry, fsk_router, ask_fn) -## ##print("using confirmed join discovery") -## ## -## -## def ask(address, message): -## MSG = "Do you want to register to device: %s? " % str(address) -## try: -## if message != None: -## print(message) -## y = raw_input(MSG) -## ## -## except AttributeError: -## y = input(MSG) -## ## -## if y == "": return True -## y = y.upper() -## if y in ['Y', 'YES']: return True -## return False -## ## -## #TODO: Might move this to energenie.init() so that it is optional -## #will make it possible to run all the test cases together also. -## ## -## # Default discovery mode, unless changed by app -## ##discovery_none() -## ##discovery_auto() -## ##discovery_ask(ask) -## discovery_autojoin() -## ##discovery_askjoin(ask) - # END diff --git a/src/energenie/Registry_test.py b/src/energenie/Registry_test.py index 25b00dd..5e1dccb 100644 --- a/src/energenie/Registry_test.py +++ b/src/energenie/Registry_test.py @@ -39,8 +39,6 @@ class TestRegistry(unittest.TestCase): - #----- HERE ----- - @test_0 def test_create(self): """Make a registry file by calling methods, and see that the file is the correct format""" @@ -83,7 +81,7 @@ # get device intances, this will cause receive routes to be knitted up tv = registry.get("tv") fan = registry.get("fan") - fsk_router.list() #### FAIL no routes created by registry + fsk_router.list() @test_0 # DONE @@ -152,7 +150,7 @@ # ->MIHO005.incoming_message() # #it should update voltage, power etc - ## poor mans incoming synthetic message + # poor mans incoming synthetic message report = OpenThings.Message(Devices.MIHO005_REPORT) diff --git a/src/energenie/TwoBit.py b/src/energenie/TwoBit.py index ea3fa63..01a0c1c 100644 --- a/src/energenie/TwoBit.py +++ b/src/energenie/TwoBit.py @@ -83,7 +83,7 @@ def encode_switch_message(state, device_address=ALL_SOCKETS, house_address=None): # -> list of numbers """Build a message to turn a switch on or off""" - #print("build: state:%s, device:%d, house:%s" % (str(state), device_address, str(house_address))) + ##print("build: state:%s, device:%d, house:%s" % (str(state), device_address, str(house_address))) if house_address == None: house_address = DEFAULT_ADDR @@ -130,17 +130,17 @@ bits |= 0x02 payload += encode_bits(bits, 4) - #print("encoded as:%s" % ashex(payload)) + ##print("encoded as:%s" % ashex(payload)) return payload def encode_bytes(data): # -> list of numbers """Turn a list of bytes into a modulated pattern equivalent""" - #print("modulate_bytes: %s" % ashex(data)) + ##print("modulate_bytes: %s" % ashex(data)) payload = [] for b in data: payload += encode_bits(b, 8) - #print(" returns: %s" % ashex(payload)) + ##print(" returns: %s" % ashex(payload)) return payload @@ -152,16 +152,16 @@ # 128 64 32 16 8 4 2 1 # 1 B B 0 1 A A 0 # i.e. a 0 is a short pulse, a 1 is a long pulse - #print("modulate_bits %s (%s)" % (ashex(data), str(number))) + ##print("modulate_bits %s (%s)" % (ashex(data), str(number))) shift = number-2 encoded = [] for i in range(int(number/2)): bits = (data >> shift) & 0x03 - #print(" shift %d bits %d" % (shift, bits)) + ##print(" shift %d bits %d" % (shift, bits)) encoded.append(ENCODER[bits]) shift -= 2 - #print(" returns:%s" % ashex(encoded)) + ##print(" returns:%s" % ashex(encoded)) return encoded @@ -192,7 +192,7 @@ def decode_bits(bits, number): # -> list of bytes, decoded bits # decode 'number' of bits held in 'bits' and return as a list of 1 or more bytes # e.g. decode_bits(0xEE, 2) -> 0b00000011 - pass # TODO + pass #TODO diff --git a/src/energenie/__init__.py b/src/energenie/__init__.py index 2e9560a..933ea1f 100644 --- a/src/energenie/__init__.py +++ b/src/energenie/__init__.py @@ -123,10 +123,10 @@ if message != None: print(message) y = raw_input(MSG) -## + except AttributeError: y = input(MSG) -## + if y == "": return True y = y.upper() if y in ['Y', 'YES']: return True diff --git a/src/energenie/crypto.py b/src/energenie/crypto.py index 3fbea58..8be85d2 100644 --- a/src/energenie/crypto.py +++ b/src/energenie/crypto.py @@ -5,13 +5,6 @@ ran = None -#static uint16_t ran; - -#void seed(uint8_t pid, uint16_t pip) -#{ -# ran = ((((uint16_t) pid) << 8) ^ pip); -#} - def init(pid, pip): """Initialise the crypto engine state variables""" @@ -19,17 +12,6 @@ ran = (((pid&0xFF)<<8) ^ pip) & 0xFFFF # maintain U16 -#uint8_t crypt(uint8_t dat) -#{ -# unsigned char i; //(u8) - -# for (i = 0; i < 5; ++i) -# { -# ran = (ran & 1) ? ((ran >> 1) ^ 62965U) : (ran >> 1); -# } -# return (uint8_t)(ran ^ dat ^ 90U); -#} - def cryptByte(data): """crypt a byte of data and update the crypto engine state variable""" global ran diff --git a/src/energenie/lifecycle.py b/src/energenie/lifecycle.py index bb6564f..c3c7d5a 100644 --- a/src/energenie/lifecycle.py +++ b/src/energenie/lifecycle.py @@ -37,12 +37,12 @@ def test_0(m): - #print("test disabled:%s" % m) - #def run(*args, **kwargs): - # print("running:%s" % m) - # r = m(*args, **kwargs) - # print("finished:%s" % m) - # return r + ##print("test disabled:%s" % m) + ##def run(*args, **kwargs): + ## print("running:%s" % m) + ## r = m(*args, **kwargs) + ## print("finished:%s" % m) + ## return r def nothing(*args, **kwargs): print("test disabled:%s" % m) diff --git a/src/energenie/radio.py b/src/energenie/radio.py index b2afc0c..84f50fd 100644 --- a/src/energenie/radio.py +++ b/src/energenie/radio.py @@ -53,7 +53,7 @@ MAX_RX_SIZE = 66 -#TODO RADIO_RESULT_XX +#TODO: RADIO_RESULT_XX def trace(msg): print(str(msg)) @@ -223,28 +223,28 @@ #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 +##@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():