def handle_message(self, payload): # check if it's time to refresh readings now = time.time() if self.voltageReadingPeriod != None and ( self.lastVoltageReading == None or now - self.lastVoltageReading > self.voltageReadingPeriod): self.queue_message(OpenThings.Message(MIHO013_BATTERY_LEVEL)) self.lastVoltageReading = now if self.diagnosticsReadingPeriod != None and ( self.lastDiagnosticsReading == None or now - self.lastDiagnosticsReading > self.diagnosticsReadingPeriod): self.queue_message(OpenThings.Message(MIHO013_DIAGNOSTICS)) self.lastDiagnosticsReading = now # send a message whilst receive window is open if len(self.send_queue) > 0: message = self.send_queue.pop(0) self.send_message(message) #print ("MIHO013 send %s",self.device_id) #extract data from message for rec in payload["recs"]: paramid = rec["paramid"] if "value" in rec: value = rec["value"] #print("MIHO013 new data %s %s %s" % (self.device_id, OpenThings.paramid_to_paramname(paramid), value)) if paramid == OpenThings.PARAM_TEMPERATURE: self.readings.ambient_temperature = value if paramid == OpenThings.PARAM_VOLTAGE: self.readings.battery_voltage = value if paramid == OpenThings.PARAM_DIAGNOSTICS: self.readings.diagnostic_flags = value
def init(): """Start the Energenie system running""" global registry, fsk_router, ook_router radio.init() OpenThings.init(Devices.CRYPT_PID) fsk_router = Registry.Router("fsk") #OOK receive not yet written #It will be used to be able to learn codes from Energenie legacy hand remotes ##ook_router = Registry.Router("ook") registry = Registry.DeviceRegistry() registry.set_fsk_router(fsk_router) ##registry.set_ook_router(ook_router path = os.path.join(sys.path[0], registry.DEFAULT_FILENAME) if os.path.isfile(path): registry.load_from(path) print("loaded registry from file") registry.list() fsk_router.list() # Default discovery mode, unless changed by app ##discovery_none() ##discovery_auto() ##discovery_ask(ask) discovery_autojoin()
def init(): """Start the Energenie system running""" global registry, fsk_router, ook_router radio.init() OpenThings.init(Devices.CRYPT_PID) fsk_router = Registry.Router("fsk") #OOK receive not yet written #It will be used to be able to learn codes from Energenie legacy hand remotes ##ook_router = Registry.Router("ook") registry = Registry.DeviceRegistry() registry.set_fsk_router(fsk_router) ##registry.set_ook_router(ook_router if os.path.isfile(registry.DEFAULT_FILENAME): registry.load_from(registry.DEFAULT_FILENAME) print("loaded registry from file") registry.list() fsk_router.list() # Default discovery mode, unless changed by app ##discovery_none() ##discovery_auto() ##discovery_ask(ask) discovery_autojoin()
def send_join_ack(radio, mfrid, productid, sensorid): # send back a JOIN ACK, so that join light stops flashing response = OpenThings.alterMessage(JOIN_ACK, header_mfrid=mfrid, header_productid=productid, header_sensorid=sensorid) p = OpenThings.encode(response) radio.transmitter() radio.transmit(p, inner_times=2) radio.receiver()
def transmit(payload, outer_times=1, inner_times=8, outer_delay=0): """Transmit a single payload using the present modulation scheme""" #Note, this optionally does a mode change before and after #extern void radio_transmit(uint8_t* payload, uint8_t len, uint8_t repeats); if DEBUG: print("***TX %s" % payload) import OpenThings if payload[0] < 20: # crude way to reject ook messages print("PAYLOAD: %s" % OpenThings.decode(payload)) # remember that the sensorId is encrypted too framelen = len(payload) if framelen < 1 or framelen > 255: raise ValueError("frame len must be 1..255") if outer_times < 1: raise ValueError("outer_times must be >0") if inner_times < 1 or inner_times > 255: raise ValueError("tx times must be 0..255") framelen = len(payload) Frame = ctypes.c_ubyte * framelen txframe = Frame(*payload) inner_times = ctypes.c_ubyte(inner_times) for i in range(outer_times): #TODO: transmit() will mode change if required #this means that outer_times will keep popping and pushing the mode #that might be ok, as it will force all the flags to clear? radio_transmit_fn(txframe, framelen, inner_times) if outer_delay != 0: time.sleep(outer_delay)
def loop(receive_time=1): radio.receiver(fsk=True) timeout = time.time() + receive_time handled = False while True: if radio.is_receive_waiting(): payload = radio.receive_cbp() now = time.time() try: msg = OpenThings.decode(payload, receive_timestamp=now) hdr = msg["header"] mfr_id = hdr["mfrid"] product_id = hdr["productid"] device_id = hdr["sensorid"] address = (mfr_id, product_id, device_id) msg_list = msg["recs"] handled = True except OpenThings.OpenThingsException: pass # print("Can't decode payload:%s" % payload) now = time.time() if now > timeout: break # print("handled: {handled}".format(handled=handled)) if handled: return msg_list else: return handled
def loop(receive_time=1): """Handle receive processing""" radio.receiver(fsk=True) timeout = time.time() + receive_time handled = False while True: if radio.is_receive_waiting(): payload = radio.receive_cbp() now = time.time() try: msg = OpenThings.decode(payload, receive_timestamp=now) hdr = msg["header"] mfr_id = hdr["mfrid"] product_id = hdr["productid"] device_id = hdr["sensorid"] address = (mfr_id, product_id, device_id) registry.fsk_router.incoming_message(address, msg) handled = True except OpenThings.OpenThingsException: print("Can't decode payload:%s" % payload) now = time.time() if now > timeout: break return handled
def turn_on(self): #TODO: header construction should be in MiHomeDevice as it is shared? payload = OpenThings.Message(SWITCH) payload.set(header_productid=self.product_id, header_sensorid=self.device_id, recs_SWITCH_STATE_value=True) return self.send_message(payload) # tx_silence remaining
def get_join_req(mfrid, productid, deviceid): """Used for testing, synthesises a JOIN_REQ message from this device""" msg = OpenThings.Message(JOIN_REQ) msg["header_mfrid"] = mfrid msg["header_productid"] = productid msg["header_sensorid"] = deviceid return msg
def receive(self, radio_config=None): # -> (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 pass # TODO #TODO: set radio to receive mode #TODO: merge radio_params with self.tx_defaults #TODO: configure radio modulation based on merged params #TODO: poll radio at rate until timeout or received #TODO: start timeout timer payload = None radio.receiver(fsk=True) while True: # timer not expired if radio.is_receive_waiting(): 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 #if crc check passes... break #TODO: inter-try delay #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? #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() address = (m, p, d) return (radio_measurements, address, payload)
def send(self, payload, radio_config=None): # payload is a pydict suitable for OpenThings # radio_params is an overlay on top of radio tx defaults p = OpenThings.encode(payload) # Set radio defaults, if no override outer_times = self.tx_defaults.outer_times outer_delay = self.tx_defaults.outer_delay inner_times = self.tx_defaults.inner_times # Merge any wanted radio params, if provided if radio_config != None: try: outer_times = radio_config.outer_times except AttributeError: pass try: outer_delay = radio_config.outer_delay except AttributeError: pass try: inner_times = radio_config.inner_times except AttributeError: pass radio.transmitter(fsk=True) ##print("inner times %s" % inner_times) radio.transmit(p, outer_times=outer_times, inner_times=inner_times, outer_delay=outer_delay)
def turn_off(self): #TODO: header construction should be in MiHomeDevice as it is shared? payload = OpenThings.Message(SWITCH) payload.set(header_productid=self.product_id, header_sensorid=self.device_id, recs_SWITCH_STATE_value=False) self.send_message(payload)
def set_setpoint_temperature(self, temperature): self.readings.setpoint_temperature = temperature; payload = OpenThings.Message(MIHO013_SET_TEMPERATURE).copyof() if temperature<0: temperature=0 if temperature>30: temperature=30 payload.set(recs_TEMPERATURE_value=int(temperature*256)) self.queue_message(payload)
def test_rx_seq(self): """Test that the rx sequence increments on each received message""" fan = Devices.DeviceFactory.get_device_from_name("AdaptorPlus", device_id=0x68b) msg = OpenThings.Message(Devices.MIHO005_REPORT) print(fan.get_receive_count()) fan.incoming_message(msg) print(fan.get_receive_count())
def join_ack(self): """Send a join-ack to the real device""" msg = OpenThings.Message(header_mfrid=MFRID_ENERGENIE, header_productid=self.product_id, header_sensorid=self.device_id) msg[OpenThings.PARAM_JOIN] = { "wr": False, "typeid": OpenThings.Value.UINT, "length": 0 } return self.send_message(msg) # tx_silence remaining
def join_ack(self): """Send a join-ack to the real device""" print "send join ack" #msg = OpenThings.Message(header_mfrid=MFRID_ENERGENIE, header_productid=self.product_id, header_sensorid=self.device_id) #msg[OpenThings.PARAM_JOIN] = {"wr":False, "typeid":OpenThings.Value.UINT, "length":0} #self.send_message(msg) payload = OpenThings.Message(JOIN_ACK) payload.set(header_productid=self.product_id, header_sensorid=self.device_id) self.send_message(payload)
def receive(self, radio_config=None ): # -> (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 pass # TODO #TODO: set radio to receive mode #TODO: merge radio_params with self.tx_defaults #TODO: configure radio modulation based on merged params #TODO: poll radio at rate until timeout or received #TODO: start timeout timer payload = None radio.receiver(fsk=True) while True: # timer not expired if radio.is_receive_waiting(): 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 #if crc check passes... break #TODO: inter-try delay #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? #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() address = (m, p, d) return (radio_measurements, address, payload)
def set_identify(self): self.queue_message(OpenThings.Message(MIHO013_IDENTIFY).copyof())
def init(): """Start the Energenie system running""" radio.DEBUG = True radio.init() OpenThings.init(Devices.CRYPT_PID)
def set_valve_position(self, position): payload = OpenThings.Message(MIHO013_SET_VALVE_POSITION).copyof() payload.set(recs_VALVE_POSITION_value=position) self.queue_message(payload)
def no(a,b): return False def yes(a,b): return True d = JoinConfirmedDiscovery(self.registry, self.fsk_router, no) # Discovery ASK JOIN(NO) # Poke synthetic unknown JOIN into the router and let it route to unknown handler msg = Devices.MIHO005.get_join_req(UNKNOWN_SENSOR_ID) self.fsk_router.incoming_message( (Devices.MFRID_ENERGENIE, Devices.PRODUCTID_MIHO005, UNKNOWN_SENSOR_ID), msg) # expect reject self.registry.list() self.fsk_router.list() d = JoinConfirmedDiscovery(self.registry, self.fsk_router, yes) # Discovery ASK JOIN(YES) self.fsk_router.incoming_message( (Devices.MFRID_ENERGENIE, Devices.PRODUCTID_MIHO005, UNKNOWN_SENSOR_ID), msg) # expect auto accept and join_ack logic to fire self.registry.list() self.fsk_router.list() if __name__ == "__main__": import OpenThings OpenThings.init(Devices.CRYPT_PID) unittest.main() # END