Exemplo n.º 1
0
class openhab_mqtt_protocol:
    processcnt = 1

    def __init__(self):
        self._log = core._log
        self._log.debug("Protocol: openhab mqtt contruction")
        self._lock = Event()
        # release lock, ready for next loop
        self._lock.clear()

    def init(self, protocol):
        self._log.debug("Protocol " + name + ": Init")
        self._client_id = protocol['client_id']
        self._server = protocol['hostname']
        self._port = protocol['port']
        self._user = protocol['user']
        self._password = protocol['password']
        self._queue_out1 = {}
        self._queue_out2 = {}
        self._queue_out3 = {}
        self._queue_out = protocol[
            'publish']  #### was commented out AJ, now back in
        self._pubstr = protocol['publish']  #### added AJ
        self._queue_in = protocol['subscribe']
        self._mq = MQTTClient(self._client_id, self._server, self._port,
                              self._user, self._password)
        # Print diagnostic messages when retries/reconnects happens
        self._mq.DEBUG = True
        self._queue = queues.Queue(maxsize=100)
        return self._queue

    def connect(self):
        self._log.debug("Protocol: " + name + ": connect")
        return self._mq.reconnect()

    def disconnect(self):
        self._log.debug("Protocol: " + name + ": disconnect")
        self._mq.disconnect()

    def check(self):
        self._log.debug("Protocol: " + name + ": check")
        self._mq.check_msg()

    def status(self):
        self._log.debug("Protocol: " + name + ": status")
        self._mq.ping()

    def recieve(self):
        self._log.debug("Protocol: " + name + ": recieve")
        self._mq.subscribe(self.queue_in)

    def send(self, devicedata):
        self._log.debug("Protocol: " + name + ": send " + devicedata["stype"])
        # connect or reconnect to mqtt server
        self.connect()
        mqttdata1 = None
        mqttdata2 = None
        mqttdata3 = None

        # case - all sensor types
        while True:
            mqttdata1 = None  # looks like duplication of above!
            mqttdata1 = {}
            mqttdata2 = {}
            mqttdata3 = {}
            self._queue_out1 = ''
            self._queue_out2 = ''
            self._queue_out3 = ''
            message1 = ''
            message2 = ''
            message3 = ''

            # Get next plugin datavalues from utils.py, plugin_senddata(self, queuedata)
            try:
                devicedata['unitname'] = self._queue.get_nowait()
                devicedata['devicename'] = self._queue.get_nowait()
            except Exception as e:
                self._log.debug("Protocol: " + name +
                                " SENSOR_TYPE_SINGLE exception: " + repr(e))
                break

            # case SENSOR_TYPE_SINGLE
            if devicedata["stype"] == core.SENSOR_TYPE_SINGLE:
                # get plugin values
                devicedata['valueV1'] = self._queue.get_nowait()
                devicedata['valueN1'] = self._queue.get_nowait()
                # Assemble mqtt message
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                break

            # case SENSOR_TYPE_LONG
            if devicedata["stype"] == core.SENSOR_TYPE_LONG:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_LONG")
                break

            # case SENSOR_TYPE_DUAL
            if devicedata["stype"] == core.SENSOR_TYPE_DUAL:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_DUAL")
                break

            # case SENSOR_TYPE_TEMP_HUM
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_TEMP_HUM")
                # Get plugin values
                try:
                    devicedata['valueV1'] = self._queue.get_nowait()
                    devicedata['valueN1'] = self._queue.get_nowait()
                    devicedata['valueV2'] = self._queue.get_nowait()
                    devicedata['valueN2'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol: " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM Exception: " +
                                    repr(e))
                    break

                # Assemble mqtt messages
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                mqttdata2 = {}
                mqttdata2['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN2']
                mqttdata2['msg'] = str(devicedata["valueV2"])
                message1 = str(devicedata["valueV2"])
                break

            # case SENSOR_TYPE_TEMP_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_BARO:
                self._log.debug("Protocol: " + name +
                                ": SENSOR_TYPE_TEMP_BARO")
                break

            # case SENSOR_TYPE_TEMP_HUM_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM_BARO:
                #self._log.debug("Protocol: "+name+": SENSOR_TYPE_TEMP_HUM_BARO")
                # Get plugin values
                try:
                    devicedata['valueV1'] = self._queue.get_nowait()
                    devicedata['valueN1'] = self._queue.get_nowait()
                    devicedata['valueV2'] = self._queue.get_nowait()
                    devicedata['valueN2'] = self._queue.get_nowait()
                    devicedata['valueV3'] = self._queue.get_nowait()
                    devicedata['valueN3'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol: " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM_BARO Exception: " +
                                    repr(e))
                    break
                # Assemble mqtt topics for valueV1, V2, V3
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                mqttdata2 = {}
                mqttdata2['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN2']
                mqttdata2['msg'] = str(devicedata["valueV2"])
                message2 = str(devicedata["valueV2"])
                mqttdata3 = {}
                mqttdata3['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN3']
                mqttdata3['msg'] = str(devicedata["valueV3"])
                message3 = str(devicedata["valueV3"])
                break

            # case SENSOR_TYPE_SWITCH
            if devicedata["stype"] == core.SENSOR_TYPE_SWITCH:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_SWITCH")
                # Get plugin values
                try:
                    devicedata['valueV1'] = self._queue.get_nowait()
                    devicedata['valueN1'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol: " + self._name +
                                    " SENSOR_TYPE_SWITCH Exception: " +
                                    repr(e))
                    break
                # Switches can have many values, OpenHAB (usually) only two: 1 (=on) or 0 (=off)
                switch_on = ['closed', 'press', 'double', 'long', 'on']
                switch_off = ['open', 'release', 'off']

                if devicedata["valueV1"] in switch_on:
                    devicedata["valueV1"] = 1
                elif devicedata["valueV1"] in switch_off:
                    devicedata["valueV1"] = 0
                else:
                    break

                # Assemble mqtt message
                mqttdata1 = {}
                mqttdata1['topic'] = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN1']
                mqttdata1['msg'] = str(devicedata["valueV1"])
                message1 = str(devicedata["valueV1"])
                break

            # case SENSOR_TYPE_DIMMER
            if devicedata["stype"] == core.SENSOR_TYPE_DIMMER:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_DIMMER")
                break

            # case SENSOR_TYPE_WIND
            if devicedata["stype"] == core.SENSOR_TYPE_WIND:
                self._log.debug("Protocol: " + name + ": SENSOR_TYPE_WIND")
                break

            # else UNKNOWN
            self._log.debug("Protocol " + name + ": Unknown sensor type!")
            break

        # Now publish the data to the MQTT broker/server

        # test for a user entry in webform protocol 'Publish' field; use it if it exists
        if self._pubstr != '':  # entry exists in Publish field
            self._queue_out1 = self._pubstr
            self._queue_out2 = self._pubstr
            self._queue_out3 = self._pubstr
        else:  # use "standard" format (unitname/devicename/valuename)...
            self._log.debug('Protocol: ' + name + ': "standard" topic format')
            self._queue_out1 = str(mqttdata1['topic'])
            if devicedata.get('valueN2') != None:
                self._queue_out2 = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN2']
            if devicedata.get('valueN3') != None:
                self._queue_out3 = devicedata['unitname'] + "/" + devicedata[
                    'devicename'] + "/" + devicedata['valueN3']

        # Whichever the sensor type, check if we have mqtt data to send, and if so publish it

        # publish datavalue1...
        if message1 != None:
            self._log.debug("Protocol: " + name + " Publish: Topic: " +
                            self._queue_out1 + ", Message: " + message1)
            self._mq.publish(self._queue_out1, message1)

        # publish datavalue2 (if it exists)
        if devicedata.get('valueN2') != None:
            if message2 != None:
                self._log.debug("Protocol: " + name + " Publish: Topic: " +
                                self._queue_out2 + ", Message: " + message2)
                self._mq.publish(self._queue_out2, message2)

        # publish datavalue3 (if it exists)
        if devicedata.get('valueN3') != None:
            if mqttdata3['msg'] != None:
                self._log.debug("Protocol: " + name + " Publish: Topic: " +
                                self._queue_out3 + ", Message: " + message3)
                self._mq.publish(self._queue_out3, message3)

        # we may eventually need more, for example for Dummy device (4 values)...
        # End of send #

    def process(self):
        # processing todo for protocol (main loop of protocol)
        self._log.debug("Protocol: " + name + " Processing...")
        devicedata = {}
        try:
            while True:
                message1 = self._queue.get_nowait(
                )  # keep reading from the protocol queue
                if message1 == core.QUEUE_MESSAGE_START:  # found "start" message
                    break  # ready to read devicedata values
            devicedata['stype'] = self._queue.get_nowait()  # get sensor type
            devicedata['serverid'] = self._queue.get_nowait()  # get server id
            #print("OHmqtt l 266: devicedata = ", devicedata)
            self.send(
                devicedata
            )  # go and get other datavalues as needed, and publish them to MQTT ...
        except Exception as e:
            self._log.debug("Protocol: " + name + " process Exception: " +
                            repr(e))

        # release lock, ready for next processing
        self._lock.clear()
Exemplo n.º 2
0
class IoTCClient():
    def __init__(self,
                 id_scope,
                 device_id,
                 credentials_type: IoTCConnectType,
                 credentials,
                 logger=None,
                 storage=None):
        self._device_id = device_id
        self._id_scope = id_scope
        self._credentials_type = credentials_type
        self._content_type = 'application%2Fjson'
        self._content_encoding = 'utf-8'
        self._connected = False
        self._credentials = credentials
        self._storage = storage
        self._events = {}
        self._model_id = None
        if logger is not None:
            self._logger = logger
        else:
            self._logger = ConsoleLogger(IoTCLogLevel.API_ONLY)
        self._twin_request_id = time()

    def set_content_type(self, content_type):
        self._content_type = encode_uri_component(content_type)

    def set_content_encoding(self, content_encoding):
        self._content_encoding = content_encoding

    def set_log_level(self, log_level: IoTCLogLevel):
        self._logger.set_log_level(log_level)

    def _on_message(self, topic, message):
        topic = topic.decode('utf-8')
        self._logger.debug(topic)
        if topic == HubTopics.TWIN_RES.format(200, self._twin_request_id):
            self._logger.info('Received twin: {}'.format(message))

        if topic.startswith(HubTopics.PROPERTIES):
            # desired properties
            self._logger.info(
                'Received desired property message: {}'.format(message))
            message = json.loads(message.decode('utf-8'))
            self.on_properties_update(message)

        elif topic.startswith(HubTopics.COMMANDS):
            # commands
            match = self._commands_regex.match(topic)
            if match is not None:
                if all(m is not None for m in [match.group(1),
                                               match.group(2)]):
                    command_name = match.group(1)
                    command_req = match.group(2)
                    command = Command(command_name, message)
                    try:
                        command_name_with_components = command_name.split("*")

                        if len(command_name_with_components) > 1:
                            # In a component
                            self._logger.debug("Command in a component")
                            command = Command(
                                command_name_with_components[1],
                                message,
                                component_name=command_name_with_components[0],
                            )

                        def reply_fn():
                            self._logger.debug(
                                'Acknowledging command {}'.format(
                                    command.name))
                            self._mqtt_client.publish(
                                '$iothub/methods/res/{}/?$rid={}'.format(
                                    200, command_req).encode('utf-8'), '')
                            if command.component_name is not None:
                                self.send_property({
                                    "{}".format(command.component_name): {
                                        "{}".format(command.name): {
                                            "value": command.value,
                                            "requestId": command_req
                                        }
                                    }
                                })
                            else:
                                self.send_property({
                                    "{}".format(command.name): {
                                        "value": command.value,
                                        "requestId": command_req
                                    }
                                })

                        command.reply = reply_fn
                        self._on_commands(command)
                        sleep(0.1)
                    except:
                        pass

        elif topic.startswith(
                HubTopics.ENQUEUED_COMMANDS.format(self._device_id)):
            params = topic.split(
                "devices/{}/messages/devicebound/".format(self._device_id),
                1)[1].split('&')
            for param in params:
                p = param.split('=')
                if p[0] == "method-name":
                    command_name = decode_uri_component(p[1])
                    command = Command(command_name, message)
                    try:
                        command_name_with_components = command_name.split("*")

                        if len(command_name_with_components) > 1:
                            # In a component
                            self._logger.debug("Command in a component")
                            command = Command(
                                command_name_with_components[1],
                                message,
                                component_name=command_name_with_components[0],
                            )
                    except:
                        pass

                    self._logger.debug(
                        'Received enqueued command {} with message: {}'.format(
                            command.name, command.value))
                    self._on_enqueued_commands(command)

    def connect(self, force_dps=False):
        creds = None

        if force_dps:
            self._logger.info("Refreshing credentials...")

        if self._storage is not None and force_dps is False:
            creds = self._storage.retrieve()

        if creds is None:
            prov = ProvisioningClient(self._id_scope, self._device_id,
                                      self._credentials_type,
                                      self._credentials, self._logger,
                                      self._model_id)
            creds = prov.register()

        self._mqtt_client = MQTTClient(self._device_id,
                                       creds.host,
                                       8883,
                                       creds.user,
                                       creds.password,
                                       ssl=True,
                                       keepalive=60)
        self._commands_regex = ure.compile(
            '\$iothub\/methods\/POST\/(.+)\/\?\$rid=(.+)')
        try:
            self._mqtt_client.connect(False)
            self._connected = True
            self._logger.info('Device connected!')
            if self._storage:
                self._storage.persist(creds)
            self._mqtt_client.set_callback(self._on_message)
            self._mqtt_client.subscribe(HubTopics.TWIN)
            self._mqtt_client.subscribe('{}/#'.format(HubTopics.PROPERTIES))
            self._mqtt_client.subscribe('{}/#'.format(HubTopics.COMMANDS))
            self._mqtt_client.subscribe('{}/#'.format(
                HubTopics.ENQUEUED_COMMANDS.format(self._device_id)))

            self._logger.debug(self._twin_request_id)
            self._mqtt_client.publish(
                HubTopics.TWIN_REQ.format(
                    self._twin_request_id).encode('utf-8'), '{{}}')
        except:
            self._logger.info("ERROR: Failed to connect to Hub")
            if force_dps is True:
                exit(1)
            self.connect(True)

    def is_connected(self):
        if self._connected == True:
            return True
        return False

    def set_model_id(self, model):
        self._model_id = model

    def send_property(self, payload):
        self._logger.debug('Sending properties {}'.format(json.dumps(payload)))
        self._mqtt_client.publish(
            HubTopics.PROP_REPORT.format(time()).encode('utf-8'),
            json.dumps(payload))

    def send_telemetry(self, payload, properties=None):
        topic = 'devices/{}/messages/events/?$.ct={}&$.ce={}'.format(
            self._device_id, self._content_type, self._content_encoding)
        if properties is not None:
            for prop in properties:
                topic += '&{}={}'.format(prop, properties[prop])

        self._mqtt_client.publish(topic.encode('utf-8'),
                                  json.dumps(payload).encode('utf-8'))

    def on(self, event, callback):
        self._events[event] = callback

    def listen(self):
        if not self.is_connected():
            return
        self._mqtt_client.ping()
        self._mqtt_client.wait_msg()
        sleep(0.5)

    def _handle_property_ack(
        self,
        callback,
        property_name,
        property_value,
        property_version,
        component_name=None,
    ):
        if callback is not None:
            ret = callback(property_name, property_value, component_name)
        else:
            ret = True
        if ret:
            if component_name is not None:
                self._logger.debug("Acknowledging {}".format(property_name))
                self.send_property({
                    "{}".format(component_name): {
                        "{}".format(property_name): {
                            "ac": 200,
                            "ad": "Property received",
                            "av": property_version,
                            "value": property_value,
                        }
                    }
                })
            else:
                self._logger.debug("Acknowledging {}".format(property_name))
                self.send_property({
                    "{}".format(property_name): {
                        "ac": 200,
                        "ad": "Property received",
                        "av": property_version,
                        "value": property_value,
                    }
                })
        else:
            self._logger.debug(
                'Property "{}" unsuccessfully processed'.format(property_name))

    def on_properties_update(self, patch):
        try:
            prop_cb = self._events[IoTCEvents.PROPERTIES]
        except:
            return
        # Set component at false by default
        is_component = False

        for prop in patch:
            is_component = False
            if prop == "$version":
                continue

            # check if component
            try:
                is_component = patch[prop]["__t"]
            except KeyError:
                pass
            if is_component:
                for component_prop in patch[prop]:
                    if component_prop == "__t":
                        continue
                    self._logger.debug(
                        'In component "{}" for property "{}"'.format(
                            prop, component_prop))
                    self._handle_property_ack(
                        prop_cb,
                        component_prop,
                        patch[prop][component_prop]["value"],
                        patch["$version"],
                        prop,
                    )
            else:
                self._handle_property_ack(prop_cb, prop, patch[prop]["value"],
                                          patch["$version"])

    def _cmd_resp(self, command: Command, value):
        self._logger.debug('Responding to command "{}" request'.format(
            command.name))
        self.send_property({
            '{}'.format(command.name): {
                'value': value,
                'requestId': command.request_id
            }
        })

    def _on_commands(self, command: Command):
        try:
            cmd_cb = self._events[IoTCEvents.COMMANDS]
        except KeyError:
            return

        self._logger.debug('Received command {}'.format(command.name))
        cmd_cb(command)

    def _on_enqueued_commands(self, command: Command):
        try:
            cmd_cb = self._events[IoTCEvents.ENQUEUED_COMMANDS]
        except KeyError:
            return

        self._logger.debug('Received enqueued command {}'.format(command.name))

        cmd_cb(command)
Exemplo n.º 3
0
class Service(BaseService):
    # Setup
    def __init__(self):
        super().__init__()
        self.mqtt = None
        self._log_stream = None
        self._asyncio_loop = asyncio.get_event_loop()
        self._services = {}
        self._init_mqtt()
        self._mqtt_connect()
        self._init_logging()
        self._get_updates()
        self._init_services()
        self.start_all_services()
        self._asyncio_loop.create_task(self._update_ntp())

    def _init_mqtt(self):
        MQTT_USER = env['MQTT_USER'] if 'MQTT_USER' in env.keys() else None
        MQTT_PASSWORD = env['MQTT_PASSWORD'] if 'MQTT_PASSWORD' in env.keys(
        ) else None

        # Create an mqtt client
        self.mqtt = MQTTClient(self.hardware_id,
                               env['MQTT_HOST'],
                               user=MQTT_USER,
                               password=MQTT_PASSWORD)

        self.mqtt.set_callback(self._mqtt_callback)

    def _init_logging(self):
        LOG_LOCALLY = env['LOG_LOCALLY'] if 'LOG_LOCALLY' in env.keys(
        ) else True
        self._log_stream = MQTTStream(self.mqtt, self.hardware_id, LOG_LOCALLY)

        self._asyncio_loop.create_task(self._log_stream._process_log_queue())

        # Set the log level based on the global environment variable 'LOG_LEVEL'
        log_level_string = env['LOG_LEVEL'] if 'LOG_LEVEL' in env.keys(
        ) else 'DEBUG'

        # Convert the log level `string` to the right enum
        LOG_LEVEL = logging.DEBUG
        for x in ['DEBUG', 'INFO', 'WARNING', 'ERROR']:
            if x == log_level_string:
                LOG_LEVEL = eval('logging.%s' % x)
                break

        # Make this the default log stream
        logging.basicConfig(level=LOG_LEVEL, stream=self._log_stream)

        self._asyncio_loop.create_task(self._process_mqtt_messages())

    @staticmethod
    def _mqtt_callback(topic, message):
        topic_path = topic.decode('utf-8').split('/')[1:]
        service, channel = topic_path

        # Command router
        if channel == 'commands':
            message = json.loads(message)
            _command_queue.append((service, message))

    async def _process_mqtt_messages(self):
        while True:
            try:
                self.mqtt.check_msg()
            except:
                pass
            await asyncio.sleep(0.1)

            while len(_command_queue):
                service, message = _command_queue.pop(0)
                args = message['args'] if 'args' in message.keys() else ''
                command = 'service.%s(%s)' % (message['command'], args)

                # Include `command`, `token`, and `args`, in the response
                response = {
                    'command': message['command'],
                    'token': message['token'],
                    'args': args
                }

                if message['command'] in self._services[service]._methods:
                    try:
                        response['response'] = eval(
                            command, globals(),
                            {'service': self._services[service]})
                    except Exception as e:
                        response['exception'] = repr(e)
                        self._logger.error(
                            'Remote command ("%s") caused an exception.' %
                            command)
                        sys.print_exception(e, self._log_stream)
                else:
                    response[
                        'exception'] = 'The specified command is not available.'
                    self._logger.error(
                        'Remote command ("%s") caused an exception.' % command)

                self.mqtt.publish(
                    '%s/%s/responses' % (self.hardware_id, service),
                    json.dumps(response))

            gc.collect()

    async def _update_ntp(self):
        def update():
            global _startup_time
            try:
                self._logger.info('Get NTP time')

                lock = _thread.allocate_lock()
                with lock:
                    _startup_time = ntptime.time() - time.time()

                self._logger.info('_startup_time=%s' % _startup_time)
            except Exception as e:
                self._logger.warning(e)
                sys.print_exception(e, self._log_stream)

        # Try every 10 s until we get an update
        while not _startup_time:
            update()
            await asyncio.sleep(10)
            gc.collect()

        # Afterwords, sync once per day
        while True:
            await asyncio.sleep(60 * 60 * 24)
            update()
            gc.collect()

    @requires_network
    def _wifi_connect(self):
        pass

    @requires_network
    def _mqtt_connect(self):
        self.mqtt.connect()
        self.mqtt.subscribe('%s/#' % self.hardware_id)

    @requires_network
    def _get_updates(self):
        reboot_flag = False

        # Get a list of all services
        for service in os.listdir('services'):
            if service == '__init__.py' or service.startswith('.'):
                continue

            self._logger.info('Check for updates to %s' % service)
            service_env = get_env(service)

            if 'GITHUB_URL' in service_env.keys():
                self._logger.info('GITHUB_URL=%s' % service_env['GITHUB_URL'])
                remote_module_path = service_env[
                    'PYTHON_MODULE_PATH'] if 'PYTHON_MODULE_PATH' in service_env else ''
                o = OTAUpdater(service_env['GITHUB_URL'],
                               module_path='services/%s' % service,
                               remote_module_path=remote_module_path)
                try:
                    gc.collect()
                    if o.check_for_update_to_install_during_next_reboot():
                        gc.collect()
                        o.download_and_install_update_if_available()
                        reboot_flag = True
                    gc.collect()
                except Exception as e:
                    self._logger.error("Couldn't get update info. %s" %
                                       repr(e))
                    sys.print_exception(e, self._log_stream)
            else:
                self._logger.error('No env defined for %s' % self.name)
                sys.print_exception(e, self._log_stream)

        if reboot_flag:
            self._logger.info('Updates installed. Rebooting...')
            machine.reset()

    def _init_services(self):
        self._logger.info('root environment = %s' %
                          (json.dumps(self.get_env())))

        # Get a list of all services
        for service in os.listdir('services'):
            if service == '__init__.py' or service.startswith('.'):
                continue

            try:
                if service == 'supervisor':
                    self._services[service] = self
                else:
                    # Create new service
                    exec('import %s' % service, locals())
                    self._services[service] = locals()[service].Service()
                self._logger.info('Initialized %s %s' %
                                  (self._services[service].name,
                                   self._services[service].version))
                service_env = self.get_env(service)
                self._logger.info('%s environment = %s' %
                                  (service, json.dumps(service_env)))
            except Exception as e:
                self._logger.error('Failed to initialize %s: %s' %
                                   (service, repr(e)))
                sys.print_exception(e, self._log_stream)

        self._logger.info('Start asyncio background thread.')

        # Start the asyncio loop in a background thread
        _thread.start_new_thread(self._asyncio_loop.run_forever, tuple())

    @property
    def status(self):
        return {
            name: (service.state, service.version)
            for name, service in self._services.items()
        }

    def reset(self):
        machine.reset()

    def stop_all_services(self):
        for service in self._services.values():
            service.stop()

    def start_all_services(self):
        for service in self._services.values():
            service.start()

    # This function runs continuously
    async def loop(self):
        self._logger.debug('state=%s' % self.state)

        # Keep wifi and mqtt connections alive
        try:
            self.mqtt.ping()
        except:
            # _mqtt_connect() requires wifi, so this will also reconnect wifi
            # if necessary
            self._mqtt_connect()

        gc.collect()
        self._logger.info('gc.mem_free()=%s' % gc.mem_free())

        await asyncio.sleep(60)
Exemplo n.º 4
0
class Service(BaseService):

    # Setup the fan to use a PWM frequency of 25kHz and a 50% duty cycle.
    fan = machine.PWM(machine.Pin(19, machine.Pin.OUT), freq=25000, duty=int(0.5*1023))
    ds = ds18x20.DS18X20(onewire.OneWire(machine.Pin(32)))
    onewire_addresses = {'temp_in': None,
                         'temp_out': bytearray(b'(\xd6G\x8a\x01\x00\x00\xb9'),
                         'temp_panel': bytearray(b'(\xd6ay\x97\t\x03\x8d')
                        }

    # Setup
    def __init__(self):
        super().__init__()

        self.wifi = network.WLAN(network.STA_IF)
        
        MQTT_USER = self.env['MQTT_USER'] if 'MQTT_USER' in self.env.keys() else None
        MQTT_PASSWORD = self.env['MQTT_PASSWORD'] if 'MQTT_PASSWORD' in self.env.keys() else None

        self.mqtt = MQTTClient(self.hardware_id,
                               self.env['MQTT_HOST'],
                               user=MQTT_USER,
                               password=MQTT_PASSWORD)
        self._asyncio_loop.create_task(self._blynk_event_loop())
        self._asyncio_loop.create_task(self._maintain_connections(30))
        self._asyncio_loop.create_task(self._update_sensors(10))
        self._logger.info("Scanning onewire bus...")
        self._logger.info(str(self.ds.scan()))

    def mqtt_connect(self):
        self.mqtt.connect()

    @classmethod
    def set_fan_duty_cycle(cls, value):
      duty_cycle = int(float(value) / 100 * 1023)
      cls.fan.duty(duty_cycle)

    @staticmethod
    def get_frequency():
        global interrupt_counter, counter_start_time_ms
        """
        # 1. Measure time for n pulses (blocking)
        
        interrupt_counter = 0
        start_time = time.ticks_ms()
        while interrupt_counter < n:
          pass
        t_delta = time.ticks_diff(time.ticks_ms(), start_time)
        frequency = float(n) / t_delta * 1000
        return frequency

        # 2. Measure number of pulses over a fixed period of time (blocking)
        
        interrupt_counter = 0
        t_delta_ms = 500
        time.sleep_ms(t_delta_ms)
        frequency = interrupt_counter / (t_delta_ms / 1000.0)
        return frequency

        # 3. Measure the number of pulses and time since the last time
        # this function was called (non-blocking). Values with this method seem
        # too low (gets better the longer the blynk_event_loop co-routine sleeps).
        
        current_time_ms = time.ticks_ms()
        t_delta = time.ticks_diff(current_time_ms, counter_start_time_ms)
        frequency = interrupt_counter / (t_delta / 1000.0)
        print('t_delta=%d, interrupt_counter=%d' % (t_delta, interrupt_counter))
        interrupt_counter = 0
        counter_start_time_ms = time.ticks_ms()
        return frequency
        """
        interrupt_counter = 0
        t_delta_ms = 500
        time.sleep_ms(t_delta_ms)
        frequency = interrupt_counter / (t_delta_ms / 1000.0)
        return frequency

    @classmethod
    def get_temperatures(cls, convert=True):
        output = {}
        try:
            if convert:
                cls.ds.convert_temp()
                time.sleep_ms(750)
            for label, address in cls.onewire_addresses.items():
                if address:
                    output[label] = cls.ds.read_temp(address)
                else:
                    output[label] = None
        except onewire.OneWireError:
            pass
        return output

    @classmethod
    def get_power(cls):
        global data

        # only turn on the fan if the panel temp is > 25C
        if data['temp_panel'] > 25:
            cls.set_fan_duty_cycle(75)
        else:
            cls.set_fan_duty_cycle(0)

        # Use the fan's rated flow rate from the datasheet scaled by the duty
        # cycle. We could probably calibrate this based on the tachometer signal,
        # which seems to change when the air flow is restricted.
        flow_rate = cls.fan.duty() / 100.0 * 3 # m^3/min
        """
        https://builditsolar.com/References/Measurements/CollectorPerformance.htm

        # https://www.engineeringtoolbox.com/air-properties-d_156.html
        air_density = 1.208 kg/m^3
        
        Air density increases as it is heated. Use the same correction factor
        as builditsolar.com page for now (1.208 * 0.065 / 0.075 = 1.047 kg/m^3).
        
        Qout = (flow_rate)*(air_density)*(temp_out - temp_in)*(Cp_air)
        Qout = (0.6 m^3/min)(1.047 kg/m^3)(30C - 20C)(1.006 kJ/kgK)
        Qout = (7.29 kJ/min)*(1000 J/kJ)*(1/60 min/s)
        Qout = 105 W
        """
        air_density = 1.208 * (.065 / .075)
        
        # Specific heat capacity of air (should also correct for temperature:
        # https://www.ohio.edu/mechanical/thermo/property_tables/air/air_Cp_Cv.html).
        # Should we be using Cp or Cv (constant pressure or constant volume)?
        Cp_air = 1.006 # kJ/kgK

        Qout = flow_rate * air_density * (data['temp_out'] - data['temp_in']) * Cp_air * 1000.0 / 60.0
        return Qout

    async def _update_sensors(self, sleep_s=10):
        global data

        convert_s = 0.75
        while True:
            if self.state == 'running':
                try:
                    data['fan_frequency'] = self.get_frequency()
                    data['fan_duty_cycle'] = int(self.fan.duty() / 1023.0 * 100)

                    data.update(self.get_temperatures())

                    # Hard code temp_in for now
                    data['temp_in'] = 20
                    data['power'] = self.get_power()

                    self._logger.debug(repr(data))

                    # If we're connected to the blynk server, push out updates
                    if blynk.state == BlynkLib.CONNECTED:
                        for label, pin in virtual_pins.items():
                            blynk.virtual_write(pin, data[label])

                    # Try to publish updates over mqtt
                    try:
                        self.mqtt.publish('open-solar-furnace/%s' % self.hardware_id, json.dumps(data))
                    except:
                        pass
                except Exception as e:
                    self._logger.error('Exception: %s' % repr(e))
                if sleep_s > convert_s:
                    await asyncio.sleep(sleep_s - convert_s)
            else:
              await asyncio.sleep(1)
            
            gc.collect()

    async def _blynk_event_loop(self, sleep_s=.1):
        while True:
            if self.state == 'running':
                await asyncio.sleep(sleep_s)
                try:
                    blynk.run()
                except Exception as e:
                    self._logger.error('Exception: %s' % repr(e))
            else:
                await asyncio.sleep(sleep_s)

            gc.collect()

    async def _maintain_connections(self, sleep_s):
        while True:
            if self.state == 'running':
                await asyncio.sleep(sleep_s)
                try:
                    # If wifi is down, disconnect blynk
                    if not self.wifi.isconnected():
                        self._logger.debug('blynk.disconnnect()')
                        blynk.disconnect()
                    
                    # Reconnect mqtt if ping() fails
                    try:
                        self.mqtt.ping()
                    except:
                        self._logger.debug('mqtt_connect()')
                        self.mqtt_connect()

                    # Try reconnecting blynk (if it's not disconnected and we have wifi)
                    if self.wifi.isconnected() and blynk.state == BlynkLib.DISCONNECTED:
                        self._logger.info("blynk_connect()")
                        blynk.connect()
                        await asyncio.sleep(5)
                except Exception as e:
                    self._logger.error('Exception: %s' % repr(e))
            else:
                await asyncio.sleep(1)

            gc.collect()
class IoTCClient():
    def __init__(self,
                 id_scope,
                 device_id,
                 credentials_type: IoTCConnectType,
                 credentials,
                 logger=None):
        self._device_id = device_id
        self._id_scope = id_scope
        self._credentials_type = credentials_type
        self._content_type = 'application%2Fjson'
        self._content_encoding = 'utf-8'
        self._connected = False
        self._credentials = credentials
        self._events = {}
        self._model_id = None
        if logger is not None:
            self._logger = logger
        else:
            self._logger = ConsoleLogger(IoTCLogLevel.API_ONLY)
        self._twin_request_id = time()

    def set_content_type(self, content_type):
        self._content_type = encode_uri_component(content_type)

    def set_content_encoding(self, content_encoding):
        self._content_encoding = content_encoding

    def set_log_level(self, log_level: IoTCLogLevel):
        self._logger.set_log_level(log_level)

    def _on_message(self, topic, message):
        topic = topic.decode('utf-8')
        if topic == HubTopics.TWIN_RES.format(200, self._twin_request_id):
            self._logger.info('Received twin: {}'.format(message))

        if topic.startswith(HubTopics.PROPERTIES):
            # desired properties
            self._logger.info(
                'Received desired property message: {}'.format(message))
            message = json.loads(message.decode('utf-8'))
            self.on_properties_update(message)

        elif topic.startswith(HubTopics.COMMANDS):
            # commands
            self._logger.info('Received command {} with message: {}'.format(
                topic, message))
            match = self._commands_regex.match(topic)
            if match is not None:
                if all(m is not None for m in [match.group(1),
                                               match.group(2)]):
                    command_name = match.group(1)
                    command_req = match.group(2)
                    command = Command(command_name, command_req)
                    if message is not None:
                        command.payload = message
                    self._on_commands(command)

        elif topic.startswith(
                HubTopics.ENQUEUED_COMMANDS.format(self._device_id)):
            params = topic.split(
                "devices/{}/messages/devicebound/".format(self._device_id),
                1)[1].split('&')
            for param in params:
                p = param.split('=')
                if p[0] == "method-name":
                    command_name = p[1].split("Commands%3A")[1]

            self._logger.info(
                'Received enqueued command {} with message: {}'.format(
                    command_name, message))
            command = Command(command_name, None)
            if message is not None:
                command.payload = message
            self._on_enqueued_commands(command)

    def connect(self):
        prov = ProvisioningClient(self._id_scope, self._device_id,
                                  self._credentials_type, self._credentials,
                                  self._logger, self._model_id)
        creds = prov.register()
        self._mqtt_client = MQTTClient(self._device_id,
                                       creds.host,
                                       8883,
                                       creds.user.encode('utf-8'),
                                       creds.password.encode('utf-8'),
                                       ssl=True,
                                       keepalive=60)
        self._commands_regex = ure.compile(
            '\$iothub\/methods\/POST\/(.+)\/\?\$rid=(.+)')
        self._mqtt_client.connect(False)
        self._connected = True
        self._logger.info('Device connected!')
        self._mqtt_client.set_callback(self._on_message)
        self._mqtt_client.subscribe(HubTopics.TWIN)
        self._mqtt_client.subscribe('{}/#'.format(HubTopics.PROPERTIES))
        self._mqtt_client.subscribe('{}/#'.format(HubTopics.COMMANDS))
        self._mqtt_client.subscribe('{}/#'.format(
            HubTopics.ENQUEUED_COMMANDS.format(self._device_id)))

        self._logger.debug(self._twin_request_id)
        self._mqtt_client.publish(
            HubTopics.TWIN_REQ.format(self._twin_request_id).encode('utf-8'),
            '{{}}')

    def is_connected(self):
        if self._connected == True:
            return True
        return False

    def set_model_id(self, model):
        self._model_id = model

    def send_property(self, payload):
        self._logger.debug('Sending properties {}'.format(json.dumps(payload)))
        self._mqtt_client.publish(
            HubTopics.PROP_REPORT.format(time()).encode('utf-8'),
            json.dumps(payload))

    def send_telemetry(self, payload, properties=None):
        topic = 'devices/{}/messages/events/?$.ct={}&$.ce={}'.format(
            self._device_id, self._content_type, self._content_encoding)
        if properties is not None:
            for prop in properties:
                topic += '{}={}&'.format(
                    encode_uri_component(prop),
                    encode_uri_component(properties[prop]))

            topic = topic[:-1]
        self._mqtt_client.publish(topic.encode('utf-8'),
                                  json.dumps(payload).encode('utf-8'))

    def on(self, event, callback):
        self._events[event] = callback

    def listen(self):
        if not self.is_connected():
            return
        self._mqtt_client.ping()
        self._mqtt_client.wait_msg()
        sleep(1)

    def on_properties_update(self, patch):
        try:
            prop_cb = self._events[IoTCEvents.PROPERTIES]
        except:
            return

        for prop in patch:
            if prop == '$version':
                continue
            ret = prop_cb(prop, patch[prop]['value'])
            if ret:
                self._logger.debug('Acknowledging {}'.format(prop))
                self.send_property({
                    '{}'.format(prop): {
                        "value": patch[prop]["value"],
                        'status': 'completed',
                        'desiredVersion': patch['$version'],
                        'message': 'Property received'
                    }
                })
            else:
                self._logger.debug(
                    'Property "{}" unsuccessfully processed'.format(prop))

    def _cmd_resp(self, command: Command, value):
        self._logger.debug('Responding to command "{}" request'.format(
            command.name))
        self.send_property({
            '{}'.format(command.name): {
                'value': value,
                'requestId': command.request_id
            }
        })

    def _cmd_ack(self, command: Command):
        self._logger.debug('Acknowledging command {}'.format(command.name))
        self._mqtt_client.publish(
            '$iothub/methods/res/{}/?$rid={}'.format(
                200, command.request_id).encode('utf-8'), '')

    def _on_commands(self, command: Command):
        try:
            cmd_cb = self._events[IoTCEvents.COMMANDS]
        except KeyError:
            return

        self._logger.debug('Received command {}'.format(command.name))
        self._cmd_ack(command)

        cmd_cb(command, self._cmd_resp)

    def _on_enqueued_commands(self, command: Command):
        try:
            cmd_cb = self._events[IoTCEvents.ENQUEUED_COMMANDS]
        except KeyError:
            return

        self._logger.debug('Received enqueued command {}'.format(command.name))
        self._cmd_ack(command)

        cmd_cb(command)
Exemplo n.º 6
0
class UnmanagedDevice:
    """
    An "unmanaged device" for the Watson IoT platform.

    Can be used with "Quickstart";
    see https://quickstart.internetofthings.ibmcloud.com
    """
    decoders = {}
    encoders = {}
    commands = {}

    def __init__(self,
                 org=QUICKSTART_ORG,
                 device_type=None,
                 device_id=None,
                 username='******',
                 token='',
                 port=8883,
                 clean_session=True,
                 domain=DOMAIN,
                 ssl_params=None,
                 log_level='info'):
        """
        Builds proper params for connecting to IoT platform MQTT broker.
        Registers JSON encoder & decoder.
        Creates MQTT client object, but does not connect.

        `quickstart` implies an *insecure* connection!

        `device_type` and `token` not necessary if `org` is `quickstart`.

        :param log_level: Logging level
        :type log_level: str
        :param org: IoT platform organization
        :type org: str
        :param device_type: IoT platform device type
        :type device_type: str
        :param device_id: IoT platform client identifier
        :type device_id: str
        :param username: IoT platform username
        :type username: str
        :param token: IoT platform API token
        :type token: str
        :param port: MQTT broker port
        :type port: int
        :param clean_session: Whether to use a clean session when connecting
        :type clean_session: bool
        :param domain: IoT platform domain name
        :type domain: str
        :param ssl_params: Additional SSL parameters for a secure connection
        :type ssl_params: dict
        """
        if not device_id:
            raise Exception('"device_id" parameter required')
        self.org = org

        if not self.is_quickstart:
            if not device_type:
                raise Exception('"device_type" parameter required')
            if not token:
                raise Exception('"token" parameter required')

        self.username = username
        self.token = token
        self.device_type = device_type
        self.address = '%s.messaging.%s' % (org, domain)
        self.client_id = 'd:%s:%s:%s' % (self.org, self.device_type, device_id)
        self.port = port
        self.keep_alive = 60
        self.logger = logging.getLogger(
            '%s.%s' % (self.__module__, self.__class__.__name__))
        self.logger.level = LOG_LEVELS[log_level]

        self.clean_session = clean_session
        self.ssl_params = ssl_params or {}

        self.client = MQTTClient(self.client_id,
                                 self.address,
                                 user=self.username,
                                 password=self.token,
                                 keepalive=60,
                                 ssl=self.is_secure,
                                 ssl_params=self.ssl_params)
        if self.logger.level == logging.DEBUG:
            self.client.DEBUG = True
        self.set_decoder('json', bytes_to_json)
        self.set_encoder('json', json.dumps)
        self.set_decoder('text', bytes_to_utf8)
        # noinspection PyTypeChecker
        self.set_encoder('text', str)

    @property
    def is_connected(self):
        """
        Crudely checks connectivity by pinging
        :return: Whether or not socket is alive
        :rtype: bool
        """
        try:
            self.client.ping()
            return True
        except OSError:
            return False

    def set_encoder(self, name, func):
        """
        "Registers" an encoder
        :param name: Name of encoder
        :type name: str
        :param func: Encoding function
        :type func: function
        """
        self.encoders[name] = func

    def unset_encoder(self, name):
        """
        "Un-registers" a encoder
        :param name: Name of existing encoder
        :type name: str
        """
        try:
            del self.encoders[name]
        except KeyError:
            pass

    def set_decoder(self, name, func):
        """
        "Registers" a decoder
        :param name: Name of decoder
        :type name: str
        :param func: Decoding function
        :type func: function
        """
        self.decoders[name] = func

    def unset_decoder(self, name):
        """
        "Un-registers" a decoder
        :param name: Name of existing decoder
        :type name: str
        """
        try:
            del self.decoders[name]
        except KeyError:
            pass

    def set_command(self, command_id, handler):
        """
        "Registers" a command handler (if org is not "quickstart")
        :param command_id: Command ID
        :type command_id: str
        :param handler: Command handler
        :type handler: function
        """
        if self.is_quickstart:
            raise Exception('"quickstart" org does not support commands')
        self.commands[command_id] = handler

    def unset_command(self, command_id):
        """
        "Unregisters" a command
        :param command_id: Command ID
        :type command_id: str
        """
        try:
            del self.commands[command_id]
        except KeyError:
            pass

    @property
    def is_secure(self):
        """
        Secure connection? `False` if `org` is `quickstart`
        :return: Whether or not SSL is enabled.
        :rtype: bool
        """
        return self.port == 8883 and not self.is_quickstart

    @property
    def is_quickstart(self):
        """
        Is "quickstart" org?
        :return: Whether or not `org` is `quickstart`
        :rtype: bool
        """
        return self.org == QUICKSTART_ORG

    def connect(self):
        """
        Connects to the MQTT broker.  If not a "quickstart" org,
        then subscribes to commands.
        """
        self.client.connect(self.clean_session)
        self.logger.debug('client "%s" connected to %s:%s' %
                          (self.client_id, self.address, self.port))

        if not self.is_quickstart:

            def message_callback(topic, message):
                """
                Callback executed when a msg for a subscribed topic is received
                :param topic: Raw MQTT topic
                :type topic: bytes
                :param message: Raw MQTT message
                :type message: bytes
                """
                topic = bytes_to_utf8(topic)
                matches = TOPIC_REGEX.match(topic)
                command_id = matches.group(1)
                message_format = matches.group(2)
                if message_format in self.decoders:
                    message = self.decoders[message_format](message)
                else:
                    self.logger.debug(
                        'no suitable decoder for message format "%s"' %
                        message_format)
                self.logger.debug('topic: %s\nmessage: %s' % (topic, message))
                if command_id in self.commands:
                    self.logger.info('received command "%s"' % command_id)
                    self.commands[command_id](message)
                else:
                    self.logger.warning('command "%s" received, \
but no handler registered' % command_id)

            self.client.set_callback(message_callback)
            self.client.subscribe(DEVICE_COMMAND_TOPIC)
            self.logger.debug('subscribed to device command topic: %s',
                              DEVICE_COMMAND_TOPIC)

    def publishEvent(self, event_id, payload, message_format='json', qos=0):
        """
        Publishes an event
        :param event_id: Event ID
        :type event_id: str
        :param payload: Event payload
        :type payload: Any
        :param message_format: Message format
        :type message_format: str
        :param qos: Quality of Service
        :type qos: int
        """
        if not self.is_connected:
            raise Exception('client is not connected')
        if qos == 2:
            raise Exception('QoS level 2 not implemented')
        event_id = event_id.strip()
        if message_format in self.encoders:
            payload = self.encoders[message_format](payload)
        self.client.publish('iot-2/evt/%s/fmt/%s' % (event_id, message_format),
                            payload, qos)

    def disconnect(self):
        """
        Disconnects (if connected)
        """
        try:
            self.client.disconnect()
            self.logger.warning('Closed connection to the IBM Watson \
IoT Platform')
        except OSError:
            self.logger.warning('Attempted to disconnect from a \
            disconnected socket')

    def loop(self):
        """
        Non-blocking check-for-messages.  You need to do something else
        after this, such as `time.sleep(1)`, or other meaningful work,
        if you are going to do this in a busy-loop.

        This appears unsupported in some environments (incl. unix)
        """
        self.client.check_msg()

    def sync_loop(self):
        """
        Blocking check-for-messages.  Run this in a busy-loop.
        """
        self.client.wait_msg()
Exemplo n.º 7
0
class domoticz_mqtt_protocol:
    processcnt = 1

    def __init__(self):
        self._log = core._log
        self._log.debug("Protocol: domoticz mqtt contruction")
        self._lock = Event()
        # release lock, ready for next loop
        self._lock.clear()

    def init(self, protocol):
        self._log.debug("Protocol " + name + ": Init")
        self._client_id = protocol['client_id']
        self._server = protocol['hostname']
        self._port = protocol['port']
        self._user = protocol['user']
        self._password = protocol['password']
        self._queue_out = protocol['publish']
        self._queue_in = protocol['subscribe']
        self._mq = MQTTClient(self._client_id, self._server, self._port,
                              self._user, self._password)
        # Print diagnostic messages when retries/reconnects happens
        #self._mq.DEBUG = True
        self._queue = queues.Queue(maxsize=100)
        return self._queue

    def connect(self):
        self._log.debug("Protocol " + name + ": connect")
        return self._mq.reconnect()

    def disconnect(self):
        self._log.debug("Protocol " + name + ": disconnect")
        self._mq.disconnect()

    def check(self):
        self._log.debug("Protocol " + name + ": check")
        self._mq.check_msg()

    def status(self):
        self._log.debug("Protocol " + name + ": status")
        self._mq.ping()

    def recieve(self):
        self._log.debug("Protocol " + name + ": recieve")
        self._mq.subscribe(self.queue_in)

    def send(self, devicedata):
        self._log.debug("Protocol " + name + ": send " + devicedata["stype"])
        # connect or reconnect to mqtt server
        self.connect()
        mqttdata = None
        # case
        while True:
            mqttdata = None

            # case SENSOR_TYPE_SINGLE
            if devicedata["stype"] == core.SENSOR_TYPE_SINGLE:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_SINGLE")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + name +
                                    " SENSOR_TYPE_SINGLE exception: " +
                                    repr(e))
                    break

                # Assemble mqtt message
                mqttdata = {}
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["nvalue"] = 0
                mqttdata["svalue"] = str(devicedata["value1"])
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_LONG
            if devicedata["stype"] == core.SENSOR_TYPE_LONG:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_LONG")
                break

            # case SENSOR_TYPE_DUAL
            if devicedata["stype"] == core.SENSOR_TYPE_DUAL:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_DUAL")
                break

            # case SENSOR_TYPE_TEMP_HUM
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_TEMP_HUM")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                    devicedata['value2'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM Exception: " +
                                    repr(e))
                    break
                # Assemble mqtt message
                mqttdata = {}
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["nvalue"] = 0
                mqttdata["svalue"] = str(devicedata["value1"]) + ";" + str(
                    devicedata["value2"]) + ";0"
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_TEMP_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_BARO:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_TEMP_BARO")
                break

            # case SENSOR_TYPE_TEMP_HUM_BARO
            if devicedata["stype"] == core.SENSOR_TYPE_TEMP_HUM_BARO:
                self._log.debug("Protocol " + name +
                                ": SENSOR_TYPE_TEMP_HUM_BARO")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                    devicedata['value2'] = self._queue.get_nowait()
                    devicedata['value3'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + self._name +
                                    " SENSOR_TYPE_TEMP_HUM_BARO Exception: " +
                                    repr(e))
                    break
                # Assemble mqtt message
                mqttdata = {}
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["nvalue"] = 0
                mqttdata["svalue"] = str(devicedata["value1"]) + ";" + str(
                    devicedata["value2"]) + ";0;" + str(
                        devicedata["value3"]) + ";0"
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_SWITCH
            if devicedata["stype"] == core.SENSOR_TYPE_SWITCH:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_SWITCH")
                # Get plugin values
                try:
                    devicedata['value1'] = self._queue.get_nowait()
                except Exception as e:
                    self._log.debug("Protocol " + self._name +
                                    " SENSOR_TYPE_SWITCH Exception: " +
                                    repr(e))
                    break

                # Switches can have many values, domoticz only two: on or off
                switch_on = ['closed', 'press', 'double', 'long']
                switch_off = ['open', 'release']

                if devicedata["value1"] in switch_on:
                    devicedata["value1"] = 'On'
                elif devicedata["value1"] in switch_off:
                    devicedata["value1"] = 'Off'
                else:
                    break

                # Assemble mqtt message
                mqttdata = {}
                mqttdata["command"] = "switchlight"
                mqttdata["idx"] = devicedata["serverid"]
                mqttdata["switchcmd"] = devicedata["value1"]
                message = ujson.dumps(mqttdata)
                break

            # case SENSOR_TYPE_DIMMER
            if devicedata["stype"] == core.SENSOR_TYPE_DIMMER:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_DIMMER")
                break

            # case SENSOR_TYPE_WIND
            if devicedata["stype"] == core.SENSOR_TYPE_WIND:
                self._log.debug("Protocol " + name + ": SENSOR_TYPE_WIND")
                break

            # else UNKNOWN
            self._log.debug("Protocol " + name + ": Unknown sensor type!")
            break

        if mqttdata != None:
            self._log.debug("Protocol " + name + ": Message: " + message)
            self._mq.publish(self._queue_out, message)

    def process(self):
        # processing todo for protocol
        self._log.debug("Protocol " + name + " Processing...")
        devicedata = {}
        try:
            while True:
                message = self._queue.get_nowait()
                if message == core.QUEUE_MESSAGE_START:
                    break
            devicedata['stype'] = self._queue.get_nowait()
            devicedata['serverid'] = self._queue.get_nowait()
            self.send(devicedata)
        except Exception as e:
            self._log.debug("Protocol " + name + " process Exception: " +
                            repr(e))

        # release lock, ready for next processing
        self._lock.clear()
Exemplo n.º 8
0
class MQTTHandler:
    def __init__(self, name, server):
        self.mqtt = MQTTClient(hexlify(machine.unique_id()), server)
        self.name = name
        self.actions = {}
        self.publishers = {}
        self.connect()
        self.mqtt.set_callback(self.handle_mqtt_msgs)
        self.publish_all_after_msg = True

    def connect(self):
        print('.connect() Check if MQTT is already connected')
        if self.isconnected():
            self.mqtt.disconnect()
        try:
            print('.connect() Not connected, so lets connect')
            self.mqtt.connect()
        except OSError:
            print(".connect() MQTT could not connect")
            return False

        time.sleep(3)
        if self.isconnected():
            self.resubscribe_all()
            return True
        else:
            # Some delay to avoid system getting blocked in a endless loop in case of
            # connection problems, unstable wifi etc.
            time.sleep(5)
            return False

    def isconnected(self):
        try:
            self.mqtt.ping()
        except OSError:
            print(".isconnected() MQTT not connected - Ping not successfull")
            return False
        except AttributeError:
            print(".isconnected() MQTT not connected - Ping not available")
            return False

        return True

    def publish_generic(self, name, value):
        print(".publish_generic() Publish: {0} = {1}".format(name, value))
        self.mqtt.publish(name, str(value))

    def handle_mqtt_msgs(self, topic, msg):
        print(".handle_mqtt_msgs() Received MQTT message: {0}:{1}".format(
            topic, msg))
        if topic in self.actions:
            print(".handle_mqtt_msgs() Found registered function {0}".format(
                self.actions[topic]))
            self.actions[topic](msg)
            if self.publish_all_after_msg:
                self.publish_all()

    def register_action(self, topicname, cbfunction):
        topic = self.name + b'/' + bytes(topicname, 'ascii')
        print(".register_action() Get topic {0} for {1}".format(
            topic, cbfunction))
        if self.isconnected():
            print('.register_action() MQTT connected, try to register')
            self.mqtt.subscribe(topic)
        self.actions[topic] = cbfunction

    def register_publisher(self, topicname, function):
        topic = self.name + b'/' + bytes(topicname, 'ascii')
        print(".register_publisher() Get topic {0} for {1}".format(
            topic, function))
        self.publishers[topic] = function

    def publish_all(self):
        for topic in self.publishers:
            self.publish_generic(topic, self.publishers[topic]())

    def resubscribe_all(self):
        for topic in self.actions:
            self.mqtt.subscribe(topic)