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)
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
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 enOcean_link(dict): # Public attribute packets = None def __init__(self, port, callback): # Initialize parent class super(enOcean_link, self).__init__() # Set debug mode if needed # from enocean.consolelogger import init_logging # init_logging() # Set port try: self.c = SerialCommunicator(port=port, callback=callback) self.c.start() except: print("Error opening serial port (", port, ")") raise return def stop(self): if self.c.is_alive(): self.c.stop() return
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 __init__(self, hass, ser): """Initialize the EnOcean dongle.""" 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)
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 __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 # 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 'mqtt_ssl' in self.conf: 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 'mqtt_ssl_insecure' in self.conf: logging.warning("Disabling SSL certificate verification") self.mqtt.tls_insecure_set(True) 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 __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 listen(): jeedom_socket.open() logging.debug("Start listening...") globals.COMMUNICATOR = SerialCommunicator(port=_device) globals.COMMUNICATOR.start() if globals.COMMUNICATOR.base_id is None: logging.error("No base id from enocean key, shutdown") shutdown() logging.info('The Base ID of your controler is %s.' % enocean.utils.to_hex_string( globals.COMMUNICATOR.base_id).replace(':', '')) globals.JEEDOM_COM.send_change_immediate({ 'baseid': str(enocean.utils.to_hex_string(globals.COMMUNICATOR.base_id)).replace( ':', '') }) packet = Packet(PACKET.COMMON_COMMAND, [0x03]) globals.COMMUNICATOR.send(packet) try: thread.start_new_thread(read_socket, ('socket', )) logging.debug('Read Socket Thread Launched') thread.start_new_thread(read_communicator, ('read', )) logging.debug('Read Device Thread Launched') except KeyboardInterrupt: logging.error("KeyboardInterrupt, shutdown") shutdown()
def __init__(self, port, callback): # Initialize parent class super(enOcean_link, self).__init__() # Set debug mode if needed # from enocean.consolelogger import init_logging # init_logging() # Set port try: self.c = SerialCommunicator(port=port, callback=callback) self.c.start() except: print("Error opening serial port (", port, ")") raise return
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 __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 main(): global influxClient, communicator # init_logging() communicator = SerialCommunicator(port='/dev/ttyUSB0') communicator.start() logger.info('The Base ID of your module is %s.' % enocean.utils.to_hex_string(communicator.base_id)) # connect to the database connect_to_ddbb() # endless loop receiving radio packets while communicator.is_alive(): try: # Loop to empty the queue... packet = communicator.receive.get(block=True, timeout=1) # RORG: 0xA5, FUNC: 0x02, TYPE: 0x05, Manufacturer: 0x2D # 01:80:F5:BC->FF:FF:FF:FF (-74 dBm): 0x01 ['0xa5', '0x8', '0x28', '0x2d', '0x80', '0x1', if packet.packet_type == PACKET.RADIO: if packet.rorg == RORG.BS4 and packet.sender_hex in ENOCEAN_DEVICES: meas = enocean_parse_and_publish( packet, ENOCEAN_DEVICES[packet.sender_hex]) except queue.Empty: continue except KeyboardInterrupt: logger.exception("Manually closing the application, exit") break except Exception as exc: logger.exception(exc) break time.sleep(0.1) # if we reached here, exit stop_application()
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 run(self): self.__wait() self.__get_comm() if self.comm is None: self.comm = SerialCommunicator(port=self.port, callback=self.__enqueue) logger.info("Starting EnOcean listener on port %s" % self.port) # Workaround for setting thread name coherent with plugin's thread name self.comm.name = self.name self.comm.start() if self.comm.base_id: logger.info("The Base ID of your module is %s" % utils.to_hex_string(self.comm.base_id)) self.__set_comm() self.__ready() self.__set_queue() while self.comm.is_alive(): if self.queue.qsize() > 0: packet = self.queue.get() if hasattr(packet, 'sender_hex'): if packet.sender_hex == self.addr: self.decode(packet) time.sleep(1)
class Encocean_Sat(object): def __init__(self): self.communicator = SerialCommunicator(port='/dev/ttyUSB0') self.communicator.start() 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') def monitor(self): while self.communicator.is_alive(): try: # Loop to empty the queue... packet = self.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('A %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 packet.parse_eep() for k in packet.parsed: print('B %s: %s' % (k, packet.parsed[k])) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.RPS: received = packet.parse_eep(0x02, 0x02) print(packet.sender_hex, packet.parsed['EB']['raw_value']) # udp_send.send_to_server('Enocean.' + packet.sender_hex, packet.parsed['EB']['raw_value']) dicti = { 'Name': 'Enocean.' + str(packet.sender_hex), 'Value': str(packet.parsed['EB']['raw_value']) } mqtt_publish.mqtt_pub( 'Inputs/Satellite/' + constants.name + '/Enocean/' + str(packet.sender_hex), dicti) except queue.Empty: continue except KeyboardInterrupt: break except Exception: traceback.print_exc(file=sys.stdout) break if self.communicator.is_alive(): self.communicator.stop()
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))
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
class Encocean_Sat(object): def __init__(self): self.communicator = SerialCommunicator(port='/dev/ttyUSB0') self.communicator.start() 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') def monitor(self): while self.communicator.is_alive(): try: # Loop to empty the queue... packet = self.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('A %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 packet.parse_eep() for k in packet.parsed: print('B %s: %s' % (k, packet.parsed[k])) if packet.packet_type == PACKET.RADIO and packet.rorg == RORG.RPS: received = packet.parse_eep(0x02, 0x02) print packet.sender_hex, packet.parsed['EB']['raw_value'] # udp_send.send_to_server('Enocean.' + packet.sender_hex, packet.parsed['EB']['raw_value']) dicti = {'Name': 'Enocean.'+str(packet.sender_hex), 'Value': str(packet.parsed['EB']['raw_value'])} mqtt_publish.mqtt_pub('Inputs/Satellite/' + constants.name + '/Enocean/'+str(packet.sender_hex),dicti) except queue.Empty: continue except KeyboardInterrupt: break except Exception: traceback.print_exc(file=sys.stdout) break if self.communicator.is_alive(): self.communicator.stop()
from enocean.consolelogger import init_logging from enocean.communicators.serialcommunicator import SerialCommunicator 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]))
return packet_response def assemble_radio_packet(transmitter_id, destination_id=None): return RadioPacket.create(rorg=RORG.BS4, rorg_func=0x10, rorg_type=0x03, sender=transmitter_id, destination=destination_id, SP=255, 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...
def __init__(self): self.communicator = SerialCommunicator(port='/dev/ttyUSB0') self.communicator.start()
class enocean(): def __init__(self, name): self.name = name self.wait = threading.Event() self.comm = None self.queue = queue.Queue() self.clientid = config.clientid self.deviceid = config.get(self.name, 'deviceid') self.descr = config.get(self.name, 'descr', fallback='') self.port = config.get(self.name, 'port', fallback=None) self.addr = config.get(self.name, 'addr') csvfile = config.get(self.name, 'csvmap') csvmap = WCSVMap() self.mapping = [] self.mapping = csvmap.load(csvfile, WCSVType.Raw) cache.store_meta(self.deviceid, self.name, self.descr, self.mapping) def decode(self, packet): ut = time.time() measures = {} if packet.packet_type == PACKET.RADIO_ERP1 and packet.rorg == RORG.VLD: packet.select_eep(0x05, 0x00) packet.parse_eep() for k in packet.parsed: self.__measure_map(k, packet, measures) if packet.packet_type == PACKET.RADIO_ERP1 and packet.rorg == RORG.BS4: for k in packet.parse_eep(0x02, 0x05): self.__measure_map(k, packet, measures) if packet.packet_type == PACKET.RADIO_ERP1 and packet.rorg == RORG.BS1: packet.select_eep(0x00, 0x01) packet.parse_eep() for k in packet.parsed: self.__measure_map(k, packet, measures) if packet.packet_type == PACKET.RADIO_ERP1 and packet.rorg == RORG.RPS: for k in packet.parse_eep(0x02, 0x02): self.__measure_map(k, packet, measures) if measures: data = { 'ts': ut, 'client_id': self.clientid, 'device_id': packet.sender_hex, 'measures': measures } cache.store(data) def __measure_map(self, k, packet, measures): logger.debug('Decoded radio packet type %s: %s' % (k, packet.parsed[k])) for row in self.mapping: (name, descr, unit, datatype, scale, offset) = row scale = float(scale) offset = float(offset) if name == k: if datatype in ('c', 's'): value = packet.parsed[k]['value'] else: value = round( float(packet.parsed[k]['value']) * scale, 14) + offset unit = packet.parsed[k]['unit'] descr = packet.parsed[k]['description'] measures[k] = value logger.info( 'EnOcean sensor: %s packet type: %s measure: %s (%s) %s %s' % (packet.sender_hex, packet.packet_type, name, descr, value, unit)) def __wait(self): global waits if self.port in waits: logger.debug( "Plugin %s waiting for SerialCommunicator on port %s..." % (self.name, self.port)) self.wait = waits[self.port] self.wait.wait() waits[self.port] = self.wait def __ready(self): if not self.wait.is_set(): logger.debug("Plugin %s SerialCommunicator on port %s ready" % (self.name, self.port)) self.wait.set() def __get_comm(self): global comms if self.port in comms: self.comm = comms[self.port] def __set_comm(self): global comms comms[self.port] = self.comm def __enqueue(self, packet): global queues logger.debug("Received radio packet %s" % packet) for queue in queues: queues[queue].put(packet) def __set_queue(self): global queues queues[self.name] = self.queue def run(self): self.__wait() self.__get_comm() if self.comm is None: self.comm = SerialCommunicator(port=self.port, callback=self.__enqueue) logger.info("Starting EnOcean listener on port %s" % self.port) # Workaround for setting thread name coherent with plugin's thread name self.comm.name = self.name self.comm.start() if self.comm.base_id: logger.info("The Base ID of your module is %s" % utils.to_hex_string(self.comm.base_id)) self.__set_comm() self.__ready() self.__set_queue() while self.comm.is_alive(): if self.queue.qsize() > 0: packet = self.queue.get() if hasattr(packet, 'sender_hex'): if packet.sender_hex == self.addr: self.decode(packet) time.sleep(1) def stop(self): if self.comm and self.comm.is_alive: self.comm.stop() logger.info("Stopping EnOcean listener on port %s" % self.port)
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()
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)
try: import queue except ImportError: import Queue as queue 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
from enocean import utils import traceback import sys try: 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' %
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
except ImportError: import Queue as queue 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:
#!/usr/bin/env python # -*- encoding: utf-8 -*- from enocean.consolelogger import init_logging from enocean.communicators.serialcommunicator import SerialCommunicator from enocean.communicators.utils import send_to_tcp_socket import sys import traceback try: import queue except ImportError: import Queue as queue init_logging() communicator = SerialCommunicator() communicator.start() while communicator.is_alive(): try: # Loop to empty the queue... packet = communicator.receive.get(block=True, timeout=1) send_to_tcp_socket('localhost', 9637, packet) except queue.Empty: continue except KeyboardInterrupt: break except Exception: traceback.print_exc(file=sys.stdout) break if communicator.is_alive(): communicator.stop()
except ImportError: import Queue as queue 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:
try: import queue except ImportError: import Queue as queue 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:
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 import utils import traceback import sys try: 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]))
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()