class EnOceanDongle: """Representation of an EnOcean dongle.""" def __init__(self, hass, ser): """Initialize the EnOcean dongle.""" from enocean.communicators.serialcommunicator import SerialCommunicator self.__communicator = SerialCommunicator(port=ser, callback=self.callback) self.__communicator.start() self.hass = hass self.hass.helpers.dispatcher.dispatcher_connect( SIGNAL_SEND_MESSAGE, self._send_message_callback) def _send_message_callback(self, command): """Send a command through the EnOcean dongle.""" self.__communicator.send(command) def callback(self, packet): """Handle EnOcean device's callback. This is the callback function called by python-enocan whenever there is an incoming packet. """ from enocean.protocol.packet import RadioPacket if isinstance(packet, RadioPacket): _LOGGER.debug("Received radio packet: %s", packet) self.hass.helpers.dispatcher.dispatcher_send( SIGNAL_RECEIVE_MESSAGE, packet)
class EnOceanDongle: """Representation of an EnOcean dongle.""" def __init__(self, hass, ser): """Initialize the EnOcean dongle.""" from enocean.communicators.serialcommunicator import SerialCommunicator self.__communicator = SerialCommunicator( port=ser, callback=self.callback) self.__communicator.start() self.hass = hass self.hass.helpers.dispatcher.dispatcher_connect( SIGNAL_SEND_MESSAGE, self._send_message_callback) def _send_message_callback(self, command): """Send a command through the EnOcean dongle.""" self.__communicator.send(command) def callback(self, packet): """Handle EnOcean device's callback. This is the callback function called by python-enocan whenever there is an incoming packet. """ from enocean.protocol.packet import RadioPacket if isinstance(packet, RadioPacket): _LOGGER.debug("Received radio packet: %s", packet) self.hass.helpers.dispatcher.dispatcher_send( SIGNAL_RECEIVE_MESSAGE, packet)
class EnOceanDongle(EnOceanDevice): """Representation of an EnOcean dongle.""" def __init__(self, hass, ser): """Initialize the EnOcean dongle.""" super().__init__(self) self.hass = hass self.ser = ser self.communicator = None """Connect to serial port of the EnOcean dongle.""" self.communicator = SerialCommunicator( port=self.ser, callback=self.communicator_callback) # switch off automated teach-in, as it gets caught in infinite loop for UTE self.communicator.teach_in = False self.communicator.start() self.dev_id = self.communicator.base_id _LOGGER.debug(f"[EnOceanDongle] self.dev_id: {self.dev_id}") unsubscribe_dispatcher = self.hass.helpers.dispatcher.async_dispatcher_connect( SIGNAL_SEND_PACKET, self.send_packet) self.hass.data[DOMAIN][DATA_DISPATCHERS].append(unsubscribe_dispatcher) def send_packet(self, packet): """Send command through EnOcean dongle.""" _LOGGER.debug( f"[EnOceanDongle.send_packet()] Sending packet: {packet}") self.communicator.send(packet) # Wait for response (acknowledgement): EnOcean defines timeout after 500ms packet = self.communicator.receive.get(block=True, timeout=0.5) if packet.response == RETURN_CODE.OK: _LOGGER.debug( f"[EnOceanDongle.send_packet()] Packet successfully sent") else: _LOGGER.debug( f"[EnOceanDongle.send_packet()] Error in sending packet") def communicator_callback(self, packet): """Handle EnOcean device's callback. This is the callback function called by python-enocan whenever there is an incoming packet. """ if isinstance(packet, ResponsePacket): # The callback routine does not care about response packets (acknowledgements). # Put the packet back into the queue, so the sender can care and respect timeouts. _LOGGER.debug( f"[EnOceanDongle.communicator_callback()] ResponsePacket received: {packet}" ) self.communicator.receive.put(packet) if isinstance(packet, RadioPacket): # Emit signal for each radio packet to notify the addressed device. _LOGGER.debug( f"[EnOceanDongle.communicator_callback()] RadioPacket received: {packet}" ) self.hass.helpers.dispatcher.dispatcher_send( SIGNAL_RECEIVE_PACKET, packet)
def seriallistener(): global on_raspi, sys_init, id_list senderID = None fire_event = False if(on_raspi is True): p = Packet(PACKET.COMMON_COMMAND, [0x08]) c = SerialCommunicator() c.start() c.send(p) while sys_init is True: try: p = c.receive.get(block=True, timeout=1) if(p.type == PACKET.RADIO and p.rorg == RORG.BS4): timestamp = datetime.datetime.now() senderID = p.data[5]*1677216+p.data[6]*65536+p.data[7]*256+p.data[8] if len(id_list) == 0: id_list.append(senderID) fire_event = True else: i=0 for i in range(0,len(id_list)): if id_list[i] == senderID: id_list.remove(senderID) fire_event = False break elif id_list[i] != senderID and i == (len(id_list)-1): id_list.append(senderID) fire_event = True break if fire_event is True: serialevent = pygame.event.Event(EV_SERIAL_INPUT, id=senderID, tm=timestamp) pygame.event.post(serialevent) fire_event = False except queue.Empty: continue except KeyboardInterrupt: break except Exception: traceback.print_exc(file=sys.stdout) break c.stop() else: while pygame.display.get_init() is True: pygame.event.post(serialevent) time.sleep(random.uniform(1,5))
rorg_type=0x01, sender=transmitter_id, CV=50, TMP=21.5, ES='true') init_logging() communicator = SerialCommunicator() communicator.start() print('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id)) if communicator.base_id is not None: print('Sending example package.') communicator.send(assemble_radio_packet(communicator.base_id)) # endless loop receiving radio packets while communicator.is_alive(): try: # Loop to empty the queue... packet = communicator.receive.get(block=True, timeout=1) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.BS4: # parse packet with given FUNC and TYPE for k in packet.parse_eep(0x02, 0x05): print('%s: %s' % (k, packet.parsed[k])) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.BS1: # alternatively you can select FUNC and TYPE explicitely packet.select_eep(0x00, 0x01) # parse it
def assemble_radio_packet(transmitter_id): return RadioPacket.create(rorg=RORG.BS4, rorg_func=0x20, rorg_type=0x01, sender=transmitter_id, CV=50, TMP=21.5, ES='true') init_logging() communicator = SerialCommunicator() communicator.start() print('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id)) if communicator.base_id is not None: print('Sending example package.') communicator.send(assemble_radio_packet(communicator.base_id)) # endless loop receiving radio packets while communicator.is_alive(): try: # Loop to empty the queue... packet = communicator.receive.get(block=True, timeout=1) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.BS4: # parse packet with given FUNC and TYPE for k in packet.parse_eep(0x02, 0x05): print('%s: %s' % (k, packet.parsed[k])) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.BS1: # alternatively you can select FUNC and TYPE explicitely packet.select_eep(0x00, 0x01) # parse it
class Communicator: mqtt = None enocean = None CONNECTION_RETURN_CODE = [ "connection successful", "incorrect protocol version", "invalid client identifier", "server unavailable", "bad username or password", "not authorised", ] def __init__(self, config, sensors): self.conf = config self.sensors = sensors # check for mandatory configuration if 'mqtt_host' not in self.conf or 'enocean_port' not in self.conf: raise Exception( "Mandatory configuration not found: mqtt_host/enocean_port") mqtt_port = int( self.conf['mqtt_port']) if 'mqtt_port' in self.conf else 1883 mqtt_keepalive = int(self.conf['mqtt_keepalive'] ) if 'mqtt_keepalive' in self.conf else 0 self.mqtt_prefix = self.conf['mqtt_prefix'] # setup mqtt connection client_id = self.conf[ 'mqtt_client_id'] if 'mqtt_client_id' in self.conf else '' self.mqtt = mqtt.Client(client_id=client_id) self.mqtt.on_connect = self._on_connect self.mqtt.on_disconnect = self._on_disconnect self.mqtt.on_message = self._on_mqtt_message self.mqtt.on_publish = self._on_mqtt_publish if 'mqtt_user' in self.conf: logging.info("Authenticating: " + self.conf['mqtt_user']) self.mqtt.username_pw_set(self.conf['mqtt_user'], self.conf['mqtt_pwd']) if str(self.conf.get('mqtt_ssl')) in ("True", "true", "1"): logging.info("Enabling SSL") ca_certs = self.conf[ 'mqtt_ssl_ca_certs'] if 'mqtt_ssl_ca_certs' in self.conf else None certfile = self.conf[ 'mqtt_ssl_certfile'] if 'mqtt_ssl_certfile' in self.conf else None keyfile = self.conf[ 'mqtt_ssl_keyfile'] if 'mqtt_ssl_keyfile' in self.conf else None self.mqtt.tls_set(ca_certs=ca_certs, certfile=certfile, keyfile=keyfile) if str(self.conf.get('mqtt_ssl_insecure')) in ("True", "true", "1"): logging.warning("Disabling SSL certificate verification") self.mqtt.tls_insecure_set(True) if str(self.conf.get('mqtt_debug')) in ("True", "true", "1"): self.mqtt.enable_logger() logging.debug("Connecting to host " + self.conf['mqtt_host'] + ", port " + str(mqtt_port) + ", keepalive " + str(mqtt_keepalive)) self.mqtt.connect_async(self.conf['mqtt_host'], port=mqtt_port, keepalive=mqtt_keepalive) self.mqtt.loop_start() # setup enocean communication try: self.enocean = SerialCommunicator(self.conf['enocean_port']) except serial.serialutil.SerialException: logging.exception('Serial Error') sys.exit(1) self.enocean.start() # sender will be automatically determined self.enocean_sender = None def __del__(self): if self.enocean is not None and self.enocean.is_alive(): self.enocean.stop() def __publish(self, topic, msg, retain=False): self.mqtt.publish("%s/%s" % (self.mqtt_prefix, topic), msg, retain) def _on_connect(self, mqtt_client, userdata, flags, rc): '''callback for when the client receives a CONNACK response from the MQTT server.''' if rc == 0: logging.info("Succesfully connected to MQTT broker.") # listen to enocean send requests mqtt_client.subscribe("%s/#" % self.mqtt_prefix) else: logging.error("Error connecting to MQTT broker: %s", self.CONNECTION_RETURN_CODE[rc]) def _on_disconnect(self, mqtt_client, userdata, rc): '''callback for when the client disconnects from the MQTT server.''' if rc == 0: logging.warning("Successfully disconnected from MQTT broker") else: logging.warning("Unexpectedly disconnected from MQTT broker: " + self.CONNECTION_RETURN_CODE[rc]) def _on_mqtt_message(self, mqtt_client, userdata, msg): '''the callback for when a PUBLISH message is received from the MQTT server.''' #1 parse topic %prefix%/%device% topic = msg.topic.split('/') if len(topic) < 3 or topic[2] != 'cmd': return # search for sensor for cur_sensor in self.sensors: if cur_sensor['name'] in topic[1]: rorg = cur_sensor['rorg'] rorg_func = cur_sensor['func'] rorg_type = cur_sensor['type'] payload = json.loads(msg.payload) command = payload.pop('CMD', 1) packet = RadioPacket.create(rorg=rorg, rorg_func=rorg_func, rorg_type=rorg_type, sender=self.enocean.base_id, destination=cur_sensor['address'], command=command, **payload) self.enocean.send(packet) def _on_mqtt_publish(self, mqtt_client, userdata, mid): '''the callback for when a PUBLISH message is successfully sent to the MQTT server.''' #logging.debug("Published MQTT message "+str(mid)) pass def _read_packet(self, packet): '''interpret packet, read properties and publish to MQTT''' mqtt_publish_json = True if 'mqtt_publish_json' in self.conf and self.conf[ 'mqtt_publish_json'] == "True" else False mqtt_json = {} # loop through all configured devices for cur_sensor in self.sensors: # does this sensor match? if packet.sender == cur_sensor['address']: # found sensor configured in config file mqtt_json['address'] = enocean.utils.to_hex_string( packet.sender) if str(cur_sensor.get('publish_rssi')) in ("True", "true", "1"): if mqtt_publish_json: mqtt_json['RSSI'] = packet.dBm else: self.__publish(cur_sensor['name'] + "/RSSI", packet.dBm) if not packet.learn or str( cur_sensor.get('log_learn')) in ("True", "true", "1"): # data packet received found_property = False if packet.packet_type == PACKET.RADIO and packet.rorg == cur_sensor[ 'rorg']: # radio packet of proper rorg type received; parse EEP direction = cur_sensor.get('direction') properties = packet.parse_eep(cur_sensor['func'], cur_sensor['type'], direction) # loop through all EEP properties for prop_name in properties: found_property = True cur_prop = packet.parsed[prop_name] # we only extract numeric values, either the scaled ones or the raw values for enums if isinstance(cur_prop['value'], numbers.Number): value = cur_prop['value'] else: value = cur_prop['raw_value'] # publish extracted information logging.debug("{}: {} ({})={} {}".format( cur_sensor['name'], prop_name, cur_prop['description'], cur_prop['value'], cur_prop['unit'])) retain = str( cur_sensor.get('persistent')) in ("True", "true", "1") if mqtt_publish_json: mqtt_json[prop_name] = value else: self.__publish(cur_sensor['name'] + "/" + prop_name, value, retain=retain) if not found_property: logging.warn('message not interpretable: {}'.format( cur_sensor['name'])) elif mqtt_publish_json: name = cur_sensor['name'] value = json.dumps(mqtt_json) logging.debug("{}: Sent MQTT: {}".format(name, value)) self.__publish(name, value, retain=retain) else: # learn request received logging.info("learn request not emitted to mqtt") def _reply_packet(self, in_packet, sensor): '''send enocean message as a reply to an incoming message''' # prepare addresses destination = in_packet.sender # prepare packet if 'direction' in sensor: # we invert the direction in this reply direction = 1 if sensor['direction'] == 2 else 2 else: direction = None packet = RadioPacket.create(RORG.BS4, sensor['func'], sensor['type'], direction=direction, sender=self.enocean_sender, destination=destination, learn=in_packet.learn) # assemble data based on packet type (learn / data) if not in_packet.learn: # data packet received # start with default data packet.data[1:5] = [(sensor['default_data'] >> i & 0xff) for i in (24, 16, 8, 0)] # do we have specific data to send? if 'data' in sensor: # override with specific data settings packet.set_eep(sensor['data']) else: # what to do if we have no data to send yet? logging.warn('sending default data as answer to %s', sensor['name']) else: # learn request received # copy EEP and manufacturer ID packet.data[1:5] = in_packet.data[1:5] # update flags to acknowledge learn request packet.data[4] = 0xf0 # send it logging.info('sending: {}'.format(packet)) self.enocean.send(packet) def _process_radio_packet(self, packet): # first, look whether we have this sensor configured found_sensor = False for cur_sensor in self.sensors: if packet.sender == cur_sensor['address']: found_sensor = cur_sensor # skip ignored sensors if found_sensor and 'ignore' in found_sensor and found_sensor['ignore']: return # log packet, if not disabled if str(self.conf.get('log_packets')) in ("True", "true", "1"): logging.info('received: {}'.format(packet)) # abort loop if sensor not found if not found_sensor: logging.info('unknown sensor: {}'.format( enocean.utils.to_hex_string(packet.sender))) return # interpret packet, read properties and publish to MQTT self._read_packet(packet) # check for neccessary reply if str(found_sensor.get('answer')) in ("True", "true", "1"): self._reply_packet(packet, found_sensor) def run(self): # start endless loop for listening while self.enocean.is_alive(): # Request transmitter ID, if needed if self.enocean_sender is None: self.enocean_sender = self.enocean.base_id # Loop to empty the queue... try: # get next packet if (platform.system() == 'Windows'): # only timeout on Windows for KeyboardInterrupt checking packet = self.enocean.receive.get(block=True, timeout=1) else: packet = self.enocean.receive.get(block=True) # check packet type if packet.packet_type == PACKET.RADIO: self._process_radio_packet(packet) elif packet.packet_type == PACKET.RESPONSE: response_code = RETURN_CODE(packet.data[0]) logging.info("got response packet: {}".format( response_code.name)) else: logging.info("got non-RF packet: {}".format(packet)) continue except queue.Empty: continue except KeyboardInterrupt: logging.debug("Exception: KeyboardInterrupt") break # Run finished, close MQTT client and stop Enocean thread logging.debug("Cleaning up") self.mqtt.loop_stop() self.mqtt.disconnect() self.mqtt.loop_forever() # will block until disconnect complete self.enocean.stop()
import queue except ImportError: import Queue as queue init_logging() """ '/dev/ttyUSB0' might change depending on where your device is. To prevent running the app as root, change the access permissions: 'sudo chmod 777 /dev/ttyUSB0' """ communicator = SerialCommunicator(port=u'/dev/ttyUSB0', callback=None) packet = Packet(PACKET.COMMON_COMMAND, [0x03]) communicator.daemon = True communicator.start() communicator.send(packet) while communicator.is_alive(): try: receivedPacket = communicator.receive.get(block=True, timeout=1) if receivedPacket.packet_type == PACKET.RESPONSE: print('Return Code: %s' % utils.to_hex_string(receivedPacket.data[0])) print('APP version: %s' % utils.to_hex_string(receivedPacket.data[1:5])) print('API version: %s' % utils.to_hex_string(receivedPacket.data[5:9])) print('Chip ID: %s' % utils.to_hex_string(receivedPacket.data[9:13])) print('Chip Version: %s' % utils.to_hex_string(receivedPacket.data[13:17])) print('App Description Version: %s' % utils.to_hex_string(receivedPacket.data[17:])) print('App Description Version (ASCII): %s' % str(bytearray(receivedPacket.data[17:]))) except queue.Empty: continue
def assemble_radio_packet(transmitter_id): return RadioPacket.create(rorg=RORG.BS4, func=0x20, type=0x01, sender=transmitter_id, CV=50, TMP=21.5, ES='true') init_logging() c = SerialCommunicator() c.start() # Request transmitter ID p = Packet(PACKET.COMMON_COMMAND, [0x08]) c.send(p) # Fetch the transmitter ID for sending packages. # NOT TESTED!!! # Needs testing, and if functional, a similar loop should be implemented to the communicator initialization. # This ID would then be used to send all future messages. transmitter_id = None while transmitter_id is None: try: p = c.receive.get(block=True, timeout=1) if p.type == PACKET.RESPONSE: transmitter_id = p.response_data # send custom radio packet c.send(assemble_radio_packet(transmitter_id)) break except queue.Empty:
class Communicator: mqtt = None enocean = None CONNECTION_RETURN_CODE = [ "connection successful", "incorrect protocol version", "invalid client identifier", "server unavailable", "bad username or password", "not authorised", ] def __init__(self, config, sensors): self.conf = config self.sensors = sensors # setup mqtt connection self.mqtt = mqtt.Client() self.mqtt.on_connect = self._on_connect self.mqtt.on_disconnect = self._on_disconnect self.mqtt.on_message = self._on_mqtt_message self.mqtt.on_publish = self._on_mqtt_publish if 'mqtt_user' in self.conf: logging.info("Authenticating: " + self.conf['mqtt_user']) self.mqtt.username_pw_set(self.conf['mqtt_user'], self.conf['mqtt_pwd']) self.mqtt.connect(self.conf['mqtt_host'], int(self.conf['mqtt_port'],0)) self.mqtt.loop_start() # setup enocean communication self.enocean = SerialCommunicator(self.conf['enocean_port']) self.enocean.start() # sender will be automatically determined self.enocean_sender = None def __del__(self): if self.enocean is not None and self.enocean.is_alive(): self.enocean.stop() def _on_connect(self, mqtt_client, userdata, flags, rc): '''callback for when the client receives a CONNACK response from the MQTT server.''' if rc == 0: logging.info("Succesfully connected to MQTT broker.") # listen to enocean send requests for cur_sensor in self.sensors: mqtt_client.subscribe(cur_sensor['name']+'/req/#') else: logging.error("Error connecting to MQTT broker: %s", self.CONNECTION_RETURN_CODE[rc]) def _on_disconnect(self, mqtt_client, userdata, rc): '''callback for when the client disconnects from the MQTT server.''' if rc == 0: logging.warning("Successfully disconnected from MQTT broker") else: logging.warning("Unexpectedly disconnected from MQTT broker: "+str(rc)) def _on_mqtt_message(self, mqtt_client, userdata, msg): '''the callback for when a PUBLISH message is received from the MQTT server.''' # search for sensor for cur_sensor in self.sensors: if cur_sensor['name'] in msg.topic: # store data for this sensor if 'data' not in cur_sensor: cur_sensor['data'] = {} prop = msg.topic[len(cur_sensor['name']+"/req/"):] try: cur_sensor['data'][prop] = int(msg.payload) except ValueError: logging.warning("Cannot parse int value for %s: %s", msg.topic, msg.payload) def _on_mqtt_publish(self, mqtt_client, userdata, mid): '''the callback for when a PUBLISH message is successfully sent to the MQTT server.''' #logging.debug("Published MQTT message "+str(mid)) pass def _read_packet(self, packet): '''interpret packet, read properties and publish to MQTT''' # loop through all configured devices for cur_sensor in self.sensors: # does this sensor match? if enocean.utils.combine_hex(packet.sender) == cur_sensor['address']: # found sensor configured in config file if 'publish_rssi' in cur_sensor and cur_sensor['publish_rssi']: self.mqtt.publish(cur_sensor['name']+"/RSSI", packet.dBm) if not packet.learn or ('log_learn' in cur_sensor and cur_sensor['log_learn']): # data packet received found_property = False if packet.packet_type == PACKET.RADIO and packet.rorg == cur_sensor['rorg']: # radio packet of proper rorg type received; parse EEP direction = cur_sensor['direction'] if 'direction' in cur_sensor else None properties = packet.parse_eep(cur_sensor['func'], cur_sensor['type'], direction) # loop through all EEP properties for prop_name in properties: found_property = True cur_prop = packet.parsed[prop_name] # we only extract numeric values, either the scaled ones or the raw values for enums if isinstance(cur_prop['value'], numbers.Number): value = cur_prop['value'] else: value = cur_prop['raw_value'] # publish extracted information logging.debug("{}: {} ({})={} {}".format(cur_sensor['name'], prop_name, cur_prop['description'], cur_prop['value'], cur_prop['unit'])) retain = 'persistent' in cur_sensor and cur_sensor['persistent'] self.mqtt.publish(cur_sensor['name']+"/"+prop_name, value, retain=retain) break if not found_property: logging.warn('message not interpretable: {}'.format(found_sensor['name'])) else: # learn request received logging.info("learn request not emitted to mqtt") def _reply_packet(self, in_packet, sensor): '''send enocean message as a reply to an incoming message''' # prepare addresses destination = in_packet.sender # prepare packet if 'direction' in sensor: # we invert the direction in this reply direction = 1 if sensor['direction'] == 2 else 2 else: direction = None packet = RadioPacket.create(RORG.BS4, sensor['func'], sensor['type'], direction=direction, sender=self.enocean_sender, destination=destination, learn=in_packet.learn) # assemble data based on packet type (learn / data) if not in_packet.learn: # data packet received # start with default data packet.data[1:5] = [ (sensor['default_data'] >> i & 0xff) for i in (24,16,8,0) ] # do we have specific data to send? if 'data' in sensor: # override with specific data settings packet.set_eep(sensor['data']) else: # what to do if we have no data to send yet? logging.warn('sending default data as answer to %s', sensor['name']) else: # learn request received # copy EEP and manufacturer ID packet.data[1:5] = in_packet.data[1:5] # update flags to acknowledge learn request packet.data[4] = 0xf0 # send it logging.info('sending: {}'.format(packet)) self.enocean.send(packet) def _process_radio_packet(self, packet): # first, look whether we have this sensor configured found_sensor = False for cur_sensor in self.sensors: if enocean.utils.combine_hex(packet.sender) == cur_sensor['address']: found_sensor = cur_sensor # skip ignored sensors if found_sensor and 'ignore' in found_sensor and found_sensor['ignore']: return # log packet, if not disabled if int(self.conf['log_packets']): logging.info('received: {}'.format(packet)) # abort loop if sensor not found if not found_sensor: logging.info('unknown sensor: {}'.format(enocean.utils.to_hex_string(packet.sender))) return # interpret packet, read properties and publish to MQTT self._read_packet(packet) # check for neccessary reply if 'answer' in found_sensor and found_sensor['answer']: self._reply_packet(packet, found_sensor) def run(self): # start endless loop for listening while self.enocean.is_alive(): # Request transmitter ID, if needed if self.enocean_sender is None: self.enocean_sender = self.enocean.base_id # Loop to empty the queue... try: # get next packet packet = self.enocean.receive.get(block=True, timeout=1) # check packet type if packet.packet_type == PACKET.RADIO: self._process_radio_packet(packet) elif packet.packet_type == PACKET.RESPONSE: response_code = RETURN_CODE(packet.data[0]) logging.info("got response packet: {}".format(response_code.name)) else: logging.info("got non-RF packet: {}".format(packet)) continue except queue.Empty: continue
class Communicator: """the main working class providing the MQTT interface to the enocean packet classes""" mqtt = None enocean = None CONNECTION_RETURN_CODE = [ "connection successful", "incorrect protocol version", "invalid client identifier", "server unavailable", "bad username or password", "not authorised", ] def __init__(self, config, sensors): self.conf = config self.sensors = sensors # check for mandatory configuration if 'mqtt_host' not in self.conf or 'enocean_port' not in self.conf: raise Exception("Mandatory configuration not found: mqtt_host/enocean_port") mqtt_port = int(self.conf['mqtt_port']) if 'mqtt_port' in self.conf else 1883 mqtt_keepalive = int(self.conf['mqtt_keepalive']) if 'mqtt_keepalive' in self.conf else 60 # setup mqtt connection client_id = self.conf['mqtt_client_id'] if 'mqtt_client_id' in self.conf else '' self.mqtt = mqtt.Client(client_id=client_id) self.mqtt.on_connect = self._on_connect self.mqtt.on_disconnect = self._on_disconnect self.mqtt.on_message = self._on_mqtt_message self.mqtt.on_publish = self._on_mqtt_publish if 'mqtt_user' in self.conf: logging.info("Authenticating: %s", self.conf['mqtt_user']) self.mqtt.username_pw_set(self.conf['mqtt_user'], self.conf['mqtt_pwd']) if str(self.conf.get('mqtt_ssl')) in ("True", "true", "1"): logging.info("Enabling SSL") ca_certs = self.conf['mqtt_ssl_ca_certs'] if 'mqtt_ssl_ca_certs' in self.conf else None certfile = self.conf['mqtt_ssl_certfile'] if 'mqtt_ssl_certfile' in self.conf else None keyfile = self.conf['mqtt_ssl_keyfile'] if 'mqtt_ssl_keyfile' in self.conf else None self.mqtt.tls_set(ca_certs=ca_certs, certfile=certfile, keyfile=keyfile) if str(self.conf.get('mqtt_ssl_insecure')) in ("True", "true", "1"): logging.warning("Disabling SSL certificate verification") self.mqtt.tls_insecure_set(True) if str(self.conf.get('mqtt_debug')) in ("True", "true", "1"): self.mqtt.enable_logger() logging.debug("Connecting to host %s, port %s, keepalive %s", self.conf['mqtt_host'], mqtt_port, mqtt_keepalive) self.mqtt.connect_async(self.conf['mqtt_host'], port=mqtt_port, keepalive=mqtt_keepalive) self.mqtt.loop_start() # setup enocean communication self.enocean = SerialCommunicator(self.conf['enocean_port']) self.enocean.start() # sender will be automatically determined self.enocean_sender = None def __del__(self): if self.enocean is not None and self.enocean.is_alive(): self.enocean.stop() def _on_connect(self, mqtt_client, _userdata, _flags, return_code): '''callback for when the client receives a CONNACK response from the MQTT server.''' if return_code == 0: logging.info("Succesfully connected to MQTT broker.") # listen to enocean send requests for cur_sensor in self.sensors: # logging.debug("MQTT subscribing: %s", cur_sensor['name']+'/req/#') mqtt_client.subscribe(cur_sensor['name']+'/req/#') else: logging.error("Error connecting to MQTT broker: %s", self.CONNECTION_RETURN_CODE[return_code] if return_code < len(self.CONNECTION_RETURN_CODE) else return_code) def _on_disconnect(self, _mqtt_client, _userdata, return_code): '''callback for when the client disconnects from the MQTT server.''' if return_code == 0: logging.warning("Successfully disconnected from MQTT broker") else: logging.warning("Unexpectedly disconnected from MQTT broker: %s", self.CONNECTION_RETURN_CODE[return_code] if return_code < len(self.CONNECTION_RETURN_CODE) else return_code) def _on_mqtt_message(self, _mqtt_client, _userdata, msg): '''the callback for when a PUBLISH message is received from the MQTT server.''' # search for sensor found_topic = False logging.debug("Got MQTT message: %s", msg.topic) for cur_sensor in self.sensors: if cur_sensor['name'] in msg.topic: # get message topic prop = msg.topic[len(cur_sensor['name']+"/req/"):] # do we face a send request? if prop == "send": found_topic = True logging.debug("Trigger message to: %s", cur_sensor['name']) destination = [(cur_sensor['address'] >> i*8) & 0xff for i in reversed(range(4))] # Retrieve command from MQTT message and pass it to _send_packet() command = None command_shortcut = cur_sensor.get('command') if command_shortcut: # Check MQTT message has valid data if 'data' not in cur_sensor: logging.warning('No data to send from MQTT message!') return # Check MQTT message sets the command field if command_shortcut not in cur_sensor['data']: logging.warning( 'Command field %s must be set in MQTT message!', command_shortcut) return # Retrieve command id from MQTT message command = cur_sensor['data'][command_shortcut] logging.debug('Retrieved command id from MQTT message: %s', hex(command)) self._send_packet(cur_sensor, destination, command) # Clear sent data, if requested by the send message # MQTT payload is binary data, thus we need to decode it if msg.payload.decode('UTF-8') == "clear": logging.debug('Clearing data buffer.') del cur_sensor['data'] else: found_topic = True # parse message content value = None try: value = int(msg.payload) except ValueError: logging.warning("Cannot parse int value for %s: %s", msg.topic, msg.payload) # store received data logging.debug("%s: %s=%s", cur_sensor['name'], prop, value) if 'data' not in cur_sensor: cur_sensor['data'] = {} cur_sensor['data'][prop] = value if not found_topic: logging.warning("Unexpected MQTT message: %s", msg.topic) def _on_mqtt_publish(self, _mqtt_client, _userdata, _mid): '''the callback for when a PUBLISH message is successfully sent to the MQTT server.''' #logging.debug("Published MQTT message "+str(mid)) def _get_command_id(self, packet, cur_sensor): '''interpret packet to retrieve command id from VLD packets''' # Retrieve the first defined EEP profile matching sensor RORG-FUNC-TYPE # As we take the first defined profile, this suppose that command is # ALWAYS at the same offset and ALWAYS has the same size. profile = packet.eep.find_profile( packet._bit_data, cur_sensor['rorg'], cur_sensor['func'], cur_sensor['type']) # Loop over profile contents for source in profile.contents: if not source.name: continue # Check the current shortcut matches the command shortcut if source['shortcut'] == cur_sensor.get('command'): return packet.eep._get_raw(source, packet._bit_data) # If not found, return None for default handling of the packet return None def _read_packet(self, packet): '''interpret packet, read properties and publish to MQTT''' mqtt_publish_json = 'mqtt_publish_json' in self.conf and \ self.conf['mqtt_publish_json'] in ("True", "true", "1") mqtt_json = {} # loop through all configured devices for cur_sensor in self.sensors: # does this sensor match? if enocean.utils.combine_hex(packet.sender) == cur_sensor['address']: # found sensor configured in config file if str(cur_sensor.get('publish_rssi')) in ("True", "true", "1"): if mqtt_publish_json: mqtt_json['RSSI'] = packet.dBm else: self.mqtt.publish(cur_sensor['name']+"/RSSI", packet.dBm) if not packet.learn or str(cur_sensor.get('log_learn')) in ("True", "true", "1"): retain = str(cur_sensor.get('persistent')) in ("True", "true", "1") found_property = self._handle_data_packet( packet, cur_sensor, mqtt_json if mqtt_publish_json else None, retain ) if not found_property: logging.warning("message not interpretable: %s", cur_sensor['name']) elif mqtt_publish_json: name = cur_sensor['name'] value = json.dumps(mqtt_json) logging.debug("%s: Sent MQTT: %s", name, value) self.mqtt.publish(name, value, retain=retain) else: # learn request received logging.info("learn request not emitted to mqtt") def _handle_data_packet(self, packet, sensor, mqtt_json, retain: bool): # data packet received found_property = False if packet.packet_type == PACKET.RADIO and packet.rorg == sensor['rorg']: # radio packet of proper rorg type received; parse EEP direction = sensor.get('direction') # Retrieve command from the received packet and pass it to parse_eep() command = None if sensor.get('command'): command = self._get_command_id(packet, sensor) logging.debug('Retrieved command id from packet: %s', hex(command)) # Retrieve properties from EEP properties = packet.parse_eep( sensor['func'], sensor['type'], direction, command) # loop through all EEP properties for prop_name in properties: found_property = True cur_prop = packet.parsed[prop_name] # we only extract numeric values, either the scaled ones # or the raw values for enums if isinstance(cur_prop['value'], numbers.Number): value = cur_prop['value'] else: value = cur_prop['raw_value'] # publish extracted information logging.debug("%s: %s (%s)=%s %s", sensor['name'], prop_name, cur_prop['description'], cur_prop['value'], cur_prop['unit']) retain = str(sensor.get('persistent')) in ("True", "true", "1") if mqtt_json is not None: mqtt_json[prop_name] = value else: self.mqtt.publish(f"{sensor['name']}/{prop_name}", value, retain=retain) return found_property def _reply_packet(self, in_packet, sensor): '''send enocean message as a reply to an incoming message''' # prepare addresses destination = in_packet.sender self._send_packet(sensor, destination, None, True, in_packet.data if in_packet.learn else None) def _send_packet(self, sensor, destination, command=None, negate_direction=False, learn_data=None): '''triggers sending of an enocean packet''' # determine direction indicator if 'direction' in sensor: direction = sensor['direction'] if negate_direction: # we invert the direction in this reply direction = 1 if direction == 2 else 2 else: direction = None # is this a response to a learn packet? is_learn = learn_data is not None # Add possibility for the user to indicate a specific sender address # in sensor configuration using added 'sender' field. # So use specified sender address if any if 'sender' in sensor: sender = [(sensor['sender'] >> i*8) & 0xff for i in reversed(range(4))] else: sender = self.enocean_sender try: # Now pass command to RadioPacket.create() packet = RadioPacket.create(sensor['rorg'], sensor['func'], sensor['type'], direction=direction, command=command, sender=sender, destination=destination, learn=is_learn) except ValueError as err: logging.error("Cannot create RF packet: %s", err) return # assemble data based on packet type (learn / data) if not is_learn: # data packet received # start with default data # Initialize packet with default_data if specified if 'default_data' in sensor: packet.data[1:5] = [(sensor['default_data'] >> i*8) & 0xff for i in reversed(range(4))] # do we have specific data to send? if 'data' in sensor: # override with specific data settings logging.debug("sensor data: %s", sensor['data']) packet.set_eep(sensor['data']) packet.parse_eep() # ensure that the logging output of packet is updated else: # what to do if we have no data to send yet? logging.warning('sending only default data as answer to %s', sensor['name']) else: # learn request received # copy EEP and manufacturer ID packet.data[1:5] = learn_data[1:5] # update flags to acknowledge learn request packet.data[4] = 0xf0 # send it logging.info("sending: %s", packet) self.enocean.send(packet) def _process_radio_packet(self, packet): # first, look whether we have this sensor configured found_sensor = False for cur_sensor in self.sensors: if 'address' in cur_sensor and \ enocean.utils.combine_hex(packet.sender) == cur_sensor['address']: found_sensor = cur_sensor # skip ignored sensors if found_sensor and 'ignore' in found_sensor and found_sensor['ignore']: return # log packet, if not disabled if str(self.conf.get('log_packets')) in ("True", "true", "1"): logging.info("received: %s", packet) # abort loop if sensor not found if not found_sensor: logging.info("unknown sensor: %s", enocean.utils.to_hex_string(packet.sender)) return # interpret packet, read properties and publish to MQTT self._read_packet(packet) # check for neccessary reply if str(found_sensor.get('answer')) in ("True", "true", "1"): self._reply_packet(packet, found_sensor) def run(self): """the main loop with blocking enocean packet receive handler""" # start endless loop for listening while self.enocean.is_alive(): # Request transmitter ID, if needed if self.enocean_sender is None: self.enocean_sender = self.enocean.base_id # Loop to empty the queue... try: # get next packet if platform.system() == 'Windows': # only timeout on Windows for KeyboardInterrupt checking packet = self.enocean.receive.get(block=True, timeout=1) else: packet = self.enocean.receive.get(block=True) # check packet type if packet.packet_type == PACKET.RADIO: self._process_radio_packet(packet) elif packet.packet_type == PACKET.RESPONSE: response_code = RETURN_CODE(packet.data[0]) logging.info("got response packet: %s", response_code.name) else: logging.info("got non-RF packet: %s", packet) continue except queue.Empty: continue except KeyboardInterrupt: logging.debug("Exception: KeyboardInterrupt") break # Run finished, close MQTT client and stop Enocean thread logging.debug("Cleaning up") self.mqtt.loop_stop() self.mqtt.disconnect() self.mqtt.loop_forever() # will block until disconnect complete self.enocean.stop()
return RadioPacket.create(rorg=RORG.BS4, func=0x20, type=0x01, sender=transmitter_id, CV=50, TMP=21.5, ES='true') init_logging() c = SerialCommunicator() c.start() # Request transmitter ID p = Packet(PACKET.COMMON_COMMAND, [0x08]) c.send(p) # Fetch the transmitter ID for sending packages. # NOT TESTED!!! # Needs testing, and if functional, a similar loop should be implemented to the communicator initialization. # This ID would then be used to send all future messages. transmitter_id = None while transmitter_id is None: try: p = c.receive.get(block=True, timeout=1) if p.type == PACKET.RESPONSE: transmitter_id = p.response_data # send custom radio packet c.send(assemble_radio_packet(transmitter_id)) break except queue.Empty:
TMP=10.0, learn=False) init_logging() communicator = SerialCommunicator(port="/dev/ttyUSB1") communicator.start() print('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id)) if communicator.base_id is not None: print('Sending example package.') sender = communicator.base_id sender[-1] = sender[-1] + 1 # communicator.send(create_enocean_teach_in(sender)) communicator.send(assemble_radio_packet(sender)) # endless loop receiving radio packets while communicator.is_alive(): try: # Loop to empty the queue... packet = communicator.receive.get(block=True, timeout=1) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.BS4: # parse packet with given FUNC and TYPE for k in packet.parse_eep(0x00, 0x01): print('%s: %s' % (k, packet.parsed[k])) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.BS1: # alternatively you can select FUNC and TYPE explicitely packet.select_eep(0x00, 0x01) # parse it
class Communicator: mqtt = None enocean = None CONNECTION_RETURN_CODE = [ "connection successful", "incorrect protocol version", "invalid client identifier", "server unavailable", "bad username or password", "not authorised", ] def __init__(self, config, sensors): self.conf = config self.sensors = sensors # setup mqtt connection self.mqtt = mqtt.Client() self.mqtt.on_connect = self._on_connect self.mqtt.on_disconnect = self._on_disconnect self.mqtt.on_message = self._on_mqtt_message self.mqtt.on_publish = self._on_mqtt_publish if 'mqtt_user' in self.conf: logging.info("Authenticating: " + self.conf['mqtt_user']) self.mqtt.username_pw_set(self.conf['mqtt_user'], self.conf['mqtt_pwd']) self.mqtt.connect(self.conf['mqtt_host'], int(self.conf['mqtt_port'], 0)) self.mqtt.loop_start() # setup enocean communication self.enocean = SerialCommunicator(self.conf['enocean_port']) self.enocean.start() # sender will be automatically determined self.enocean_sender = None def __del__(self): if self.enocean is not None and self.enocean.is_alive(): self.enocean.stop() def _on_connect(self, mqtt_client, userdata, flags, rc): '''callback for when the client receives a CONNACK response from the MQTT server.''' if rc == 0: logging.info("Succesfully connected to MQTT broker.") # listen to enocean send requests for cur_sensor in self.sensors: mqtt_client.subscribe(cur_sensor['name'] + '/req/#') else: logging.error("Error connecting to MQTT broker: %s", self.CONNECTION_RETURN_CODE[rc]) def _on_disconnect(self, mqtt_client, userdata, rc): '''callback for when the client disconnects from the MQTT server.''' if rc == 0: logging.warning("Successfully disconnected from MQTT broker") else: logging.warning("Unexpectedly disconnected from MQTT broker: " + str(rc)) def _on_mqtt_message(self, mqtt_client, userdata, msg): '''the callback for when a PUBLISH message is received from the MQTT server.''' # search for sensor for cur_sensor in self.sensors: if cur_sensor['name'] in msg.topic: # store data for this sensor if 'data' not in cur_sensor: cur_sensor['data'] = {} prop = msg.topic[len(cur_sensor['name'] + "/req/"):] try: cur_sensor['data'][prop] = int(msg.payload) except ValueError: logging.warning("Cannot parse int value for %s: %s", msg.topic, msg.payload) def _on_mqtt_publish(self, mqtt_client, userdata, mid): '''the callback for when a PUBLISH message is successfully sent to the MQTT server.''' #logging.debug("Published MQTT message "+str(mid)) pass def _read_packet(self, packet): '''interpret packet, read properties and publish to MQTT''' # loop through all configured devices for cur_sensor in self.sensors: # does this sensor match? if enocean.utils.combine_hex( packet.sender) == cur_sensor['address']: # found sensor configured in config file if 'publish_rssi' in cur_sensor and cur_sensor['publish_rssi']: self.mqtt.publish(cur_sensor['name'] + "/RSSI", packet.dBm) if not packet.learn or ('log_learn' in cur_sensor and cur_sensor['log_learn']): # data packet received found_property = False if packet.packet_type == PACKET.RADIO and packet.rorg == cur_sensor[ 'rorg']: # radio packet of proper rorg type received; parse EEP direction = cur_sensor[ 'direction'] if 'direction' in cur_sensor else None properties = packet.parse_eep(cur_sensor['func'], cur_sensor['type'], direction) # loop through all EEP properties for prop_name in properties: found_property = True cur_prop = packet.parsed[prop_name] # we only extract numeric values, either the scaled ones or the raw values for enums if isinstance(cur_prop['value'], numbers.Number): value = cur_prop['value'] else: value = cur_prop['raw_value'] # publish extracted information logging.debug("{}: {} ({})={} {}".format( cur_sensor['name'], prop_name, cur_prop['description'], cur_prop['value'], cur_prop['unit'])) retain = 'persistent' in cur_sensor and cur_sensor[ 'persistent'] self.mqtt.publish(cur_sensor['name'] + "/" + prop_name, value, retain=retain) break if not found_property: logging.warn('message not interpretable: {}'.format( found_sensor['name'])) else: # learn request received logging.info("learn request not emitted to mqtt") def _reply_packet(self, in_packet, sensor): '''send enocean message as a reply to an incoming message''' # prepare addresses destination = in_packet.sender # prepare packet if 'direction' in sensor: # we invert the direction in this reply direction = 1 if sensor['direction'] == 2 else 2 else: direction = None packet = RadioPacket.create(RORG.BS4, sensor['func'], sensor['type'], direction=direction, sender=self.enocean_sender, destination=destination, learn=in_packet.learn) # assemble data based on packet type (learn / data) if not in_packet.learn: # data packet received # start with default data packet.data[1:5] = [(sensor['default_data'] >> i & 0xff) for i in (24, 16, 8, 0)] # do we have specific data to send? if 'data' in sensor: # override with specific data settings packet.set_eep(sensor['data']) else: # what to do if we have no data to send yet? logging.warn('sending default data as answer to %s', sensor['name']) else: # learn request received # copy EEP and manufacturer ID packet.data[1:5] = in_packet.data[1:5] # update flags to acknowledge learn request packet.data[4] = 0xf0 # send it logging.info('sending: {}'.format(packet)) self.enocean.send(packet) def _process_radio_packet(self, packet): # first, look whether we have this sensor configured found_sensor = False for cur_sensor in self.sensors: if enocean.utils.combine_hex( packet.sender) == cur_sensor['address']: found_sensor = cur_sensor # skip ignored sensors if found_sensor and 'ignore' in found_sensor and found_sensor['ignore']: return # log packet, if not disabled if int(self.conf['log_packets']): logging.info('received: {}'.format(packet)) # abort loop if sensor not found if not found_sensor: logging.info('unknown sensor: {}'.format( enocean.utils.to_hex_string(packet.sender))) return # interpret packet, read properties and publish to MQTT self._read_packet(packet) # check for neccessary reply if 'answer' in found_sensor and found_sensor['answer']: self._reply_packet(packet, found_sensor) def run(self): # start endless loop for listening while self.enocean.is_alive(): # Request transmitter ID, if needed if self.enocean_sender is None: self.enocean_sender = self.enocean.base_id # Loop to empty the queue... try: # get next packet packet = self.enocean.receive.get(block=True, timeout=1) # check packet type if packet.packet_type == PACKET.RADIO: self._process_radio_packet(packet) elif packet.packet_type == PACKET.RESPONSE: response_code = RETURN_CODE(packet.data[0]) logging.info("got response packet: {}".format( response_code.name)) else: logging.info("got non-RF packet: {}".format(packet)) continue except queue.Empty: continue
from enocean.protocol.packet import Packet from enocean.protocol.constants import PACKET, RORG import sys import traceback try: import queue except ImportError: import Queue as queue p = Packet(PACKET.COMMON_COMMAND, [0x08]) init_logging() c = SerialCommunicator() c.start() c.send(p) while c.is_alive(): try: # Loop to empty the queue... p = c.receive.get(block=True, timeout=1) if p.type == PACKET.RADIO and p.rorg == RORG.BS4: for k in p.parse_eep(0x02, 0x05): print('%s: %s' % (k, p.parsed[k])) if p.type == PACKET.RADIO and p.rorg == RORG.BS1: for k in p.parse_eep(0x00, 0x01): print('%s: %s' % (k, p.parsed[k])) if p.type == PACKET.RADIO and p.rorg == RORG.RPS: for k in p.parse_eep(0x02, 0x04): print('%s: %s' % (k, p.parsed[k])) except queue.Empty: continue
class EnOceanDongle: """Representation of an EnOcean dongle.""" def __init__(self, hass, ser): """Initialize the EnOcean dongle.""" from enocean.communicators.serialcommunicator import SerialCommunicator self.__communicator = SerialCommunicator( port=ser, callback=self.callback) self.__communicator.start() self.__devices = [] def register_device(self, dev): """Register another device.""" self.__devices.append(dev) def send_command(self, command): """Send a command from the EnOcean dongle.""" self.__communicator.send(command) # pylint: disable=no-self-use def _combine_hex(self, data): """Combine list of integer values to one big integer.""" output = 0x00 for i, j in enumerate(reversed(data)): output |= (j << i * 8) return output def callback(self, temp): """Handle EnOcean device's callback. This is the callback function called by python-enocan whenever there is an incoming packet. """ from enocean.protocol.packet import RadioPacket if isinstance(temp, RadioPacket): _LOGGER.debug("Received radio packet: %s", temp) rxtype = None value = None if temp.data[6] == 0x30: rxtype = "wallswitch" value = 1 elif temp.data[6] == 0x20: rxtype = "wallswitch" value = 0 elif temp.data[4] == 0x0c: rxtype = "power" value = temp.data[3] + (temp.data[2] << 8) elif temp.data[2] == 0x60: rxtype = "switch_status" if temp.data[3] == 0xe4: value = 1 elif temp.data[3] == 0x80: value = 0 elif temp.data[0] == 0xa5 and temp.data[1] == 0x02: rxtype = "dimmerstatus" value = temp.data[2] for device in self.__devices: if rxtype == "wallswitch" and device.stype == "listener": if temp.sender_int == self._combine_hex(device.dev_id): device.value_changed(value, temp.data[1]) if rxtype == "power" and device.stype == "powersensor": if temp.sender_int == self._combine_hex(device.dev_id): device.value_changed(value) if rxtype == "power" and device.stype == "switch": if temp.sender_int == self._combine_hex(device.dev_id): if value > 10: device.value_changed(1) if rxtype == "switch_status" and device.stype == "switch": if temp.sender_int == self._combine_hex(device.dev_id): device.value_changed(value) if rxtype == "dimmerstatus" and device.stype == "dimmer": if temp.sender_int == self._combine_hex(device.dev_id): device.value_changed(value)
class EnOceanDongle: """Representation of an EnOcean dongle.""" def __init__(self, hass, ser): """Initialize the EnOcean dongle.""" from enocean.communicators.serialcommunicator import SerialCommunicator self.__communicator = SerialCommunicator(port=ser, callback=self.callback) self.__communicator.start() self.__devices = [] def register_device(self, dev): """Register another device.""" self.__devices.append(dev) def send_command(self, command): """Send a command from the EnOcean dongle.""" self.__communicator.send(command) # pylint: disable=no-self-use def _combine_hex(self, data): """Combine list of integer values to one big integer.""" output = 0x00 for i, j in enumerate(reversed(data)): output |= (j << i * 8) return output def callback(self, temp): """Handle EnOcean device's callback. This is the callback function called by python-enocan whenever there is an incoming packet. """ from enocean.protocol.packet import RadioPacket if isinstance(temp, RadioPacket): rxtype = None value = None if temp.data[6] == 0x30: rxtype = "wallswitch" value = 1 elif temp.data[6] == 0x20: rxtype = "wallswitch" value = 0 elif temp.data[4] == 0x0c: rxtype = "power" value = temp.data[3] + (temp.data[2] << 8) elif temp.data[2] == 0x60: rxtype = "switch_status" if temp.data[3] == 0xe4: value = 1 elif temp.data[3] == 0x80: value = 0 elif temp.data[0] == 0xa5 and temp.data[1] == 0x02: rxtype = "dimmerstatus" value = temp.data[2] for device in self.__devices: if rxtype == "wallswitch" and device.stype == "listener": if temp.sender == self._combine_hex(device.dev_id): device.value_changed(value, temp.data[1]) if rxtype == "power" and device.stype == "powersensor": if temp.sender == self._combine_hex(device.dev_id): device.value_changed(value) if rxtype == "power" and device.stype == "switch": if temp.sender == self._combine_hex(device.dev_id): if value > 10: device.value_changed(1) if rxtype == "switch_status" and device.stype == "switch": if temp.sender == self._combine_hex(device.dev_id): device.value_changed(value) if rxtype == "dimmerstatus" and device.stype == "dimmer": if temp.sender == self._combine_hex(device.dev_id): device.value_changed(value)
import queue except ImportError: import Queue as queue init_logging() """ '/dev/ttyUSB0' might change depending on where your device is. To prevent running the app as root, change the access permissions: 'sudo chmod 777 /dev/ttyUSB0' """ communicator = SerialCommunicator(port=u'/dev/ttyUSB0', callback=None) packet = Packet(PACKET.COMMON_COMMAND, [0x03]) communicator.daemon = True communicator.start() communicator.send(packet) while communicator.is_alive(): try: receivedPacket = communicator.receive.get(block=True, timeout=1) if receivedPacket.packet_type == PACKET.RESPONSE: print('Return Code: %s' % utils.to_hex_string(receivedPacket.data[0])) print('APP version: %s' % utils.to_hex_string(receivedPacket.data[1:5])) print('API version: %s' % utils.to_hex_string(receivedPacket.data[5:9])) print('Chip ID: %s' % utils.to_hex_string(receivedPacket.data[9:13])) print('Chip Version: %s' % utils.to_hex_string(receivedPacket.data[13:17]))