Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
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))
Пример #5
0
                              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
Пример #6
0
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
Пример #7
0
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
Пример #9
0
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:
Пример #10
0
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
Пример #11
0
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()
Пример #12
0
    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:
Пример #13
0
                              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
Пример #14
0
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
Пример #15
0
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
Пример #16
0
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)
Пример #17
0
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)
Пример #18
0
    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]))