Example #1
0
    def __init__(
        self, locationId, locationData, mqttclient: MqttClient, api: IotCloudApi
    ):
        self.locationId = locationId
        self.locationName = ""
        self.postalCode = 0
        self.timeZone = ""
        self.devices: typing.Dict[str, Device] = {}
        self.devicesLock = Lock()
        self.timeZone = ""

        # Manage the location dinamic data like the weather or the sun schedule
        self.dataManager = LocationDataManager(locationId, api)

        # Location status check data
        self.offlineInitialTimestamp = 0
        self.timeFilter = 50  # Seconds
        self.notificationSent = False
        self.api = api

        # Set location data
        self.setLocationData(locationData, mqttclient)

        # Set MQTT topics and handlers
        self.updatedDeviceTopic = f"v1/{self.locationId}/+/updatedDevice"
        mqttclient.message_callback_add(
            self.updatedDeviceTopic, self.onDeviceUpdated)

        logger.info(
            f"Created location: {self.locationName} with {len(self.devices)} devices")
Example #2
0
def ensure_mqtt(host):
    mqtt_client = MQTTClient()
    mqtt_client.on_connect = mqtt_on_connect
    mqtt_client.message_callback_add('embrace/sensors', mqtt_on_message_sensor)
    mqtt_client.message_callback_add('embrace/topology',
                                     mqtt_on_message_topology)
    mqtt_client.connect(host, 1883, 60)
    return mqtt_client
def on_connect(mqttc: mqtt.Client, inlfuxc, flags, rc):
    logging.info(f"MQTT connection established ({rc})")

    # subscribe to match signal cbor messages
    topic_matched_cbor = "jonas-rpi-00001/radiotracking/matched/cbor"
    mqttc.subscribe(topic_matched_cbor)
    mqttc.message_callback_add(topic_matched_cbor, on_matched_cbor)
    logging.info(f"Subscribed to {topic_matched_cbor}")
Example #4
0
class MQTTSnipsComponent(SnipsComponent):
    """A Snips component using the MQTT protocol directly.

    Attributes:
        snips (:class:`.SnipsConfig`): The Snips configuration.
        mqtt (`paho.mqtt.client.Client`_): The MQTT client object.

    .. _`paho.mqtt.client.Client`: https://www.eclipse.org/paho/clients/python/docs/#client
    """
    def _connect(self):
        """Connect with the MQTT broker referenced in the Snips configuration
        file.
        """
        self.mqtt = Client()
        self.mqtt.on_connect = self._subscribe_topics
        connect(self.mqtt, self.snips.mqtt)

    def _start(self):
        """Start the event loop to the MQTT broker so the component starts
        listening to MQTT topics and the callback methods are called.
        """
        self.mqtt.loop_forever()

    def _subscribe_topics(self, client, userdata, flags, connection_result):
        """Subscribe to the MQTT topics we're interested in.

        Each method with an attribute set by a
        :func:`snipskit.decorators.mqtt.topic` decorator is registered as a
        callback for the corresponding topic.
        """
        for name in dir(self):
            callable_name = getattr(self, name)
            if hasattr(callable_name, 'topic'):
                self.mqtt.subscribe(getattr(callable_name, 'topic'))
                self.mqtt.message_callback_add(getattr(callable_name, 'topic'),
                                               callable_name)

    def publish(self, topic, payload, json_encode=True):
        """Publish a payload on an MQTT topic on the MQTT broker of this object.

        Args:
            topic (str): The MQTT topic to publish the payload on.
            payload (str): The payload to publish.
            json_encode (bool, optional): Whether or not the payload is a dict
                that will be encoded as a JSON string. The default value is
                True. Set this to False if you want to publish a binary payload
                as-is.

        Returns:
            :class:`paho.mqtt.MQTTMessageInfo`: Information about the
            publication of the message.

        .. versionadded:: 0.5.0
        """
        if json_encode:
            payload = json.dumps(payload)

        return self.mqtt.publish(topic, payload)
def on_connect(mqttc: mqtt.Client, inlfuxc, flags, rc):
    schedule.run_pending()
    logging.info(f"MQTT connection established ({rc})")

    # subscribe to signal cbor messages
    topic_signal_cbor = "+/radiotracking/device/+/cbor"
    mqttc.subscribe(topic_signal_cbor)
    mqttc.message_callback_add(topic_signal_cbor, on_signal_cbor)
    logging.info(f"Subscribed to {topic_signal_cbor}")

    # subscribe to match signal cbor messages
    topic_matched_cbor = "+/radiotracking/matched/cbor"
    mqttc.subscribe(topic_matched_cbor)
    mqttc.message_callback_add(topic_matched_cbor, on_matched_cbor)
    logging.info(f"Subscribed to {topic_matched_cbor}")

    # subscribe to log csv messages
    topic_log_csv = "+/radiotracking/log/csv"
    mqttc.subscribe(topic_log_csv)
    mqttc.message_callback_add(topic_log_csv, on_log_csv)
    logging.info(f"Subscribed to {topic_log_csv}")

    # subscribe to util messages
    topic_mqtt_util = "+/mqttutil/#"
    mqttc.subscribe(topic_mqtt_util)
    mqttc.message_callback_add(topic_mqtt_util, on_mqtt_util)
    logging.info(f"Subscribed to {topic_mqtt_util}")
Example #6
0
 def __init__(self, client: mclient.Client, topic: str, callback, logger: logging.Logger, QOS=0):
     super().__init__()
     if not callable(callback):
         raise AttributeError("callback muss aufrufbar sein.")
     self.name = "MsgThr"
     self.setDaemon(False)
     self._cancel_new_when_running = False
     self._sleeping = True
     self._callback = callback
     self._message_queue = queue.Queue()
     self._logger = logger.getChild("mmThread-{}".format(topic))
     self._mutex = thr.Lock()
     self.start()
     client.subscribe(topic, qos=QOS)
     client.message_callback_add(topic, self.__mqtt_callback)
     self._kill = False
Example #7
0
    def __init__(self, baseTopic: str, sensorId: str, metadata: typing.Dict,
                 mqttclient: MqttClient,
                 locationData: LocationDataManager) -> None:
        super().__init__(baseTopic, sensorId, metadata, mqttclient,
                         locationData)

        self.state = False

        self.setSensorData(metadata, mqttclient)

        # Set up the relevant MQTT topics
        self.stateTopic = f"{baseTopic}{sensorId}/state"
        self.setStateTopic = f"{baseTopic}{sensorId}/setState"
        mqttclient.message_callback_add(self.stateTopic, self.onDeviceState)

        # Enable the retrieval of the sun schedule
        locationData.registerSunSchedule()
Example #8
0
    def _on_connect(self, client: Client, userdata, flags, rc) -> None:
        client.subscribe("ald/sample/temperature")
        client.message_callback_add('ald/sample/temperature',
                                    self._on_sample_temperature)

        client.subscribe("ald/flow/state")
        client.message_callback_add('ald/flow/state', self._on_flow_state)

        client.subscribe("ald/pressure/main")
        client.message_callback_add('ald/pressure/main',
                                    self._on_pressure_main)

        client.subscribe("ald/io/state")
        client.message_callback_add('ald/io/state', self._on_valves)

        for topic in self.TEMPERATURE_TOPICS:
            client.subscribe("ald/temperature/{}".format(topic))
            client.message_callback_add("ald/temperature/{}".format(topic),
                                        self._on_temperature)

        debug_print("Connected.")
Example #9
0
def callback_servidor(mqttc, userdata, msg):
    '''
    maneja la conexión inicial con el servidor hasta que recibe permiso,
    y las desconexiones inesperadas del servidor desconectando a todos los jugadores
    '''
    if msg.payload == b"SERVER_FAIL":
        print("SERVER_FAIL: se ha caido el servidor. Por favor, introduzca 0.")
        mqttc.disconnect()
        conectado.value = 0
    elif msg.payload == b"SERVER_READY":
        sleep(random() * 10)
        mqttc.publish(choques + "/servidor/" + userdata[0],
                      payload="CONNECT_REQUEST")
    elif msg.payload == b"CONNECT_ACCEPT":
        print("SERVIDOR ACTIVO")
        mqttc.unsubscribe(choques + "/servidor/exception")
        mqttc.unsubscribe(choques + "/servidor/" + userdata[0])
        mqttc.publish(choques + "/solicitudes", payload=userdata[0])
    elif msg.payload == b"USER_EXC":
        print("Usuario no válido")
        print("Prueba otro usuario que no este en uso")
        mqttc.disconnect()
        nombre_usuario = input("¿nombre usuario? ")
        sleep(1)
        mqttc = Client(userdata=[nombre_usuario, 0, 0])  #,clean_session=True)
        mqttc.message_callback_add(choques + "/servidor/#", callback_servidor)
        mqttc.message_callback_add(choques + "/partidas/#", callback_partidas)
        mqttc.message_callback_add(choques + "/jugadores/" + nombre_usuario,
                                   callback_jugadores)
        mqttc.will_set(choques + "/jugadores/" + nombre_usuario,
                       payload="DISCONNECT")
        mqttc.connect(broker)
        mqttc.subscribe(choques + "/jugadores/" + nombre_usuario)
        mqttc.subscribe(choques + "/servidor")
        mqttc.subscribe(choques + "/servidor/" + nombre_usuario)
        mqttc.subscribe(choques + "/servidor/exception")
        mqttc.publish(choques + "/servidor/" + nombre_usuario,
                      payload="CONNECT_REQUEST")
        mqttc.loop_start()
class CubeSerializingSocket(object):

    CUBE_TOPIC_NAME = 'RawCubes'

    def __init__(self, on_receive_cube_fn=None):
        self.on_receive_cube_fn = on_receive_cube_fn

        # https://www.infoq.com/articles/practical-mqtt-with-paho
        self.mqtt_client = MqttClient()

        if self.on_receive_cube_fn is not None:

            def on_connect(client, userdata, flags, rc):
                self.mqtt_client.subscribe(
                    CubeSerializingSocket.CUBE_TOPIC_NAME)

            self.mqtt_client.on_connect = on_connect
            self.mqtt_client.message_callback_add(
                CubeSerializingSocket.CUBE_TOPIC_NAME,
                self.receive_zipped_cube)

        self.mqtt_client.connect('127.0.0.1')
        self.mqtt_client.loop_start()

    def send_zipped_cube(self, cube_dict):
        dict = cPickle.dumps(cube_dict, protocol=cPickle.HIGHEST_PROTOCOL)
        dict = zlib.compress(dict)
        logging.debug("SEND Cube: Topic: {}".format(
            CubeSerializingSocket.CUBE_TOPIC_NAME))
        return self.mqtt_client.publish(CubeSerializingSocket.CUBE_TOPIC_NAME,
                                        bytearray(dict))

    def receive_zipped_cube(self, client, userdata, msg):
        data = msg.payload
        pobj = zlib.decompress(data)
        pobj = cPickle.loads(pobj)
        self.on_receive_cube_fn(msg.topic, pobj)
Example #11
0
    def _on_connect(self, client: Client, userdata, flags, rc) -> None:
        client.subscribe("ald/sample/temperature")
        client.message_callback_add('ald/sample/temperature',
                                    self._on_sample_temperature)

        client.subscribe("ald/flow/state")
        client.message_callback_add('ald/flow/state', self._on_flow_state)

        client.subscribe("ald/pressure/main")
        client.message_callback_add('ald/pressure/main',
                                    self._on_pressure_main)

        debug_print("Connected.")
Example #12
0
    def __init__(self, baseTopic: str, sensorId: str, metadata: typing.Dict,
                 mqttclient: MqttClient,
                 locationData: LocationDataManager) -> None:
        super().__init__(baseTopic, sensorId, metadata, mqttclient,
                         locationData)

        # Runtime variables
        self.state = False
        self.tempReferences: dict[str, float] = {}
        self.tempRefValues: dict[str, TempValue] = {}
        self.heating = False
        self.setHeatingMem = False
        self.alarm = False

        # Default settings
        self.startHeatingAt = int(time.time())
        self.setpoint = 20.0
        self.stateChanged = False
        self.hysteresisHigh = -0.1
        self.hysteresisLow = -0.8
        self.maxHeatingTime = 3600 * 8  # 8 hours
        self.tempReferenceMem = 0.0
        self.progThermostatShutdownEnabled = False
        self.progThermostatShutdownTime = 0
        self.progThermostatShutdownMem = False
        self.filterTime = 90  # seconds
        self.startHeatingfilterTime = 0
        self.stopHeatingfilterTime = 0

        self.setSensorData(metadata, mqttclient)

        # Set up the relevant MQTT topics
        self.stateTopic = f"{baseTopic}{sensorId}/state"
        self.auxTopic = f"{baseTopic}{sensorId}/aux/"
        self.heatingTopic = self.auxTopic + "heating"
        self.setpointTopic = self.auxTopic + "setpoint"
        self.ackAlarmTopic = self.auxTopic + "ackAlarm"
        self.setStateTopic = f"{baseTopic}{sensorId}/setState"
        mqttclient.message_callback_add(self.stateTopic, self.onDeviceState)
        mqttclient.message_callback_add(self.heatingTopic, self.onHeating)
        mqttclient.message_callback_add(self.setpointTopic, self.onSetpoint)
        mqttclient.message_callback_add(self.ackAlarmTopic, self.onAckAlarm)

        self.addTempReference(mqttclient, f"{baseTopic}{sensorId}/value", 2.0)

        # Enable the retrieval of the sun schedule
        locationData.registerSunSchedule()
Example #13
0
class Mqtt():
    """Main Mqtt class.

    :param app:  flask application object
    :param connect_async:  if True then connect_aync will be used to connect to MQTT broker
    :param mqtt_logging: if True then messages from MQTT client will be logged

    """
    def __init__(self, app=None, connect_async=False, mqtt_logging=False):
        # type: (Flask, bool, bool) -> None
        self.app = app
        self._connect_async = connect_async  # type: bool
        self._connect_handler = None  # type: Optional[Callable]
        self._disconnect_handler = None  # type: Optional[Callable]
        self.topics = {}  # type: Dict[str, TopicQos]
        self.connected = False
        self.client = Client()
        if mqtt_logging:
            self.client.enable_logger(logger)

        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        # type: (Flask) -> None
        """Init the Flask-MQTT addon."""
        self.client_id = app.config.get("MQTT_CLIENT_ID", "")
        self.clean_session = app.config.get("MQTT_CLEAN_SESSION", True)

        if isinstance(self.client_id, unicode):
            self.client._client_id = self.client_id.encode('utf-8')
        else:
            self.client._client_id = self.client_id

        self.client._clean_session = self.clean_session
        self.client._transport = app.config.get("MQTT_TRANSPORT",
                                                "tcp").lower()
        self.client._protocol = app.config.get("MQTT_PROTOCOL_VERSION",
                                               MQTTv311)

        self.client.on_connect = self._handle_connect
        self.client.on_disconnect = self._handle_disconnect
        self.username = app.config.get("MQTT_USERNAME")
        self.password = app.config.get("MQTT_PASSWORD")
        self.broker_url = app.config.get("MQTT_BROKER_URL", "localhost")
        self.broker_port = app.config.get("MQTT_BROKER_PORT", 1883)
        self.tls_enabled = app.config.get("MQTT_TLS_ENABLED", False)
        self.keepalive = app.config.get("MQTT_KEEPALIVE", 60)
        self.last_will_topic = app.config.get("MQTT_LAST_WILL_TOPIC")
        self.last_will_message = app.config.get("MQTT_LAST_WILL_MESSAGE")
        self.last_will_qos = app.config.get("MQTT_LAST_WILL_QOS", 0)
        self.last_will_retain = app.config.get("MQTT_LAST_WILL_RETAIN", False)

        if self.tls_enabled:
            self.tls_ca_certs = app.config["MQTT_TLS_CA_CERTS"]
            self.tls_certfile = app.config.get("MQTT_TLS_CERTFILE")
            self.tls_keyfile = app.config.get("MQTT_TLS_KEYFILE")
            self.tls_cert_reqs = app.config.get("MQTT_TLS_CERT_REQS",
                                                ssl.CERT_REQUIRED)
            self.tls_version = app.config.get("MQTT_TLS_VERSION",
                                              ssl.PROTOCOL_TLSv1)
            self.tls_ciphers = app.config.get("MQTT_TLS_CIPHERS")
            self.tls_insecure = app.config.get("MQTT_TLS_INSECURE", False)

        # set last will message
        if self.last_will_topic is not None:
            self.client.will_set(
                self.last_will_topic,
                self.last_will_message,
                self.last_will_qos,
                self.last_will_retain,
            )

        self._connect()

    def _connect(self):
        # type: () -> None

        if self.username is not None:
            self.client.username_pw_set(self.username, self.password)

        # security
        if self.tls_enabled:
            self.client.tls_set(
                ca_certs=self.tls_ca_certs,
                certfile=self.tls_certfile,
                keyfile=self.tls_keyfile,
                cert_reqs=self.tls_cert_reqs,
                tls_version=self.tls_version,
                ciphers=self.tls_ciphers,
            )

            if self.tls_insecure:
                self.client.tls_insecure_set(self.tls_insecure)

        if self._connect_async:
            # if connect_async is used
            self.client.connect_async(self.broker_url,
                                      self.broker_port,
                                      keepalive=self.keepalive)
        else:
            res = self.client.connect(self.broker_url,
                                      self.broker_port,
                                      keepalive=self.keepalive)

            if res == 0:
                logger.debug("Connected client '{0}' to broker {1}:{2}".format(
                    self.client_id, self.broker_url, self.broker_port))
            else:
                logger.error(
                    "Could not connect to MQTT Broker, Error Code: {0}".format(
                        res))
        self.client.loop_start()

    def _disconnect(self):
        # type: () -> None
        self.client.loop_stop()
        self.client.disconnect()
        logger.debug('Disconnected from Broker')

    def _handle_connect(self, client, userdata, flags, rc):
        # type: (Client, Any, Dict, int) -> None
        if rc == MQTT_ERR_SUCCESS:
            self.connected = True
            for key, item in self.topics.items():
                self.client.subscribe(topic=item.topic, qos=item.qos)
        if self._connect_handler is not None:
            self._connect_handler(client, userdata, flags, rc)

    def _handle_disconnect(self, client, userdata, rc):
        # type: (str, Any, int) -> None
        self.connected = False
        if self._disconnect_handler is not None:
            self._disconnect_handler()

    def on_topic(self, topic):
        # type: (str) -> Callable
        """Decorator.

        Decorator to add a callback function that is called when a certain
        topic has been published. The callback function is expected to have the
        following form: `handle_topic(client, userdata, message)`

        :parameter topic: a string specifying the subscription topic to
            subscribe to

        The topic still needs to be subscribed via mqtt.subscribe() before the
        callback function can be used to handle a certain topic. This way it is
        possible to subscribe and unsubscribe during runtime.

        **Example usage:**::

            app = Flask(__name__)
            mqtt = Mqtt(app)
            mqtt.subscribe('home/mytopic')

            @mqtt.on_topic('home/mytopic')
            def handle_mytopic(client, userdata, message):
                print('Received message on topic {}: {}'
                      .format(message.topic, message.payload.decode()))
        """
        def decorator(handler):
            # type: (Callable[[str], None]) -> Callable[[str], None]
            self.client.message_callback_add(topic, handler)
            return handler

        return decorator

    def subscribe(self, topic, qos=0):
        # type: (str, int) -> Tuple[int, int]
        """
        Subscribe to a certain topic.

        :param topic: a string specifying the subscription topic to
            subscribe to.
        :param qos: the desired quality of service level for the subscription.
                    Defaults to 0.

        :rtype: (int, int)
        :result: (result, mid)

        A topic is a UTF-8 string, which is used by the broker to filter
        messages for each connected client. A topic consists of one or more
        topic levels. Each topic level is separated by a forward slash
        (topic level separator).

        The function returns a tuple (result, mid), where result is
        MQTT_ERR_SUCCESS to indicate success or (MQTT_ERR_NO_CONN, None) if the
        client is not currently connected.  mid is the message ID for the
        subscribe request. The mid value can be used to track the subscribe
        request by checking against the mid argument in the on_subscribe()
        callback if it is defined.

        **Topic example:** `myhome/groundfloor/livingroom/temperature`

        """
        # TODO: add support for list of topics

        # don't subscribe if already subscribed
        # try to subscribe
        result, mid = self.client.subscribe(topic=topic, qos=qos)

        # if successful add to topics
        if result == MQTT_ERR_SUCCESS:
            self.topics[topic] = TopicQos(topic=topic, qos=qos)
            logger.debug('Subscribed to topic: {0}, qos: {1}'.format(
                topic, qos))
        else:
            logger.error('Error {0} subscribing to topic: {1}'.format(
                result, topic))

        return (result, mid)

    def unsubscribe(self, topic):
        # type: (str) -> Optional[Tuple[int, int]]
        """
        Unsubscribe from a single topic.

        :param topic: a single string that is the subscription topic to
                      unsubscribe from

        :rtype: (int, int)
        :result: (result, mid)

        Returns a tuple (result, mid), where result is MQTT_ERR_SUCCESS
        to indicate success or (MQTT_ERR_NO_CONN, None) if the client is not
        currently connected.
        mid is the message ID for the unsubscribe request. The mid value can be
        used to track the unsubscribe request by checking against the mid
        argument in the on_unsubscribe() callback if it is defined.

        """
        # don't unsubscribe if not in topics
        if topic in self.topics:
            result, mid = self.client.unsubscribe(topic)

            if result == MQTT_ERR_SUCCESS:
                self.topics.pop(topic)
                logger.debug('Unsubscribed from topic: {0}'.format(topic))
            else:
                logger.debug('Error {0} unsubscribing from topic: {1}'.format(
                    result, topic))

            # if successful remove from topics
            return result, mid
        return None

    def unsubscribe_all(self):
        # type: () -> None
        """Unsubscribe from all topics."""
        topics = list(self.topics.keys())
        for topic in topics:
            self.unsubscribe(topic)

    def publish(self, topic, payload=None, qos=0, retain=False):
        # type: (str, bytes, int, bool) -> Tuple[int, int]
        """
        Send a message to the broker.

        :param topic: the topic that the message should be published on
        :param payload: the actual message to send. If not given, or set to
                        None a zero length message will be used. Passing an
                        int or float will result in the payload being
                        converted to a string representing that number.
                        If you wish to send a true int/float, use struct.pack()
                        to create the payload you require.
        :param qos: the quality of service level to use
        :param retain: if set to True, the message will be set as the
                       "last known good"/retained message for the topic

        :returns: Returns a tuple (result, mid), where result is
                  MQTT_ERR_SUCCESS to indicate success or MQTT_ERR_NO_CONN
                  if the client is not currently connected. mid is the message
                  ID for the publish request.

        """
        if not self.connected:
            self.client.reconnect()

        result, mid = self.client.publish(topic, payload, qos, retain)
        if result == MQTT_ERR_SUCCESS:
            logger.debug('Published topic {0}: {1}'.format(topic, payload))
        else:
            logger.error('Error {0} publishing topic {1}'.format(
                result, topic))

        return (result, mid)

    def on_connect(self):
        # type: () -> Callable
        """Decorator.

        Decorator to handle the event when the broker responds to a connection
        request. Only the last decorated function will be called.

        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self._connect_handler = handler
            return handler

        return decorator

    def on_disconnect(self):
        # type: () -> Callable
        """Decorator.

        Decorator to handle the event when client disconnects from broker. Only
        the last decorated function will be called.

        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self._disconnect_handler = handler
            return handler

        return decorator

    def on_message(self):
        # type: () -> Callable
        """Decorator.

        Decorator to handle all messages that have been subscribed and that
        are not handled via the `on_message` decorator.

        **Note:** Unlike as written in the paho mqtt documentation this
        callback will not be called if there exists an topic-specific callback
        added by the `on_topic` decorator.

        **Example Usage:**::

            @mqtt.on_message()
            def handle_messages(client, userdata, message):
                print('Received message on topic {}: {}'
                      .format(message.topic, message.payload.decode()))
        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_message = handler
            return handler

        return decorator

    def on_publish(self):
        # type: () -> Callable
        """Decorator.

        Decorator to handle all messages that have been published by the
        client.

        **Example Usage:**::

            @mqtt.on_publish()
            def handle_publish(client, userdata, mid):
                print('Published message with mid {}.'
                      .format(mid))
        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_publish = handler
            return handler

        return decorator

    def on_subscribe(self):
        # type: () -> Callable
        """Decorate a callback function to handle subscritions.

        **Usage:**::

            @mqtt.on_subscribe()
            def handle_subscribe(client, userdata, mid, granted_qos):
                print('Subscription id {} granted with qos {}.'
                      .format(mid, granted_qos))
        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_subscribe = handler
            return handler

        return decorator

    def on_unsubscribe(self):
        # type: () -> Callable
        """Decorate a callback funtion to handle unsubscribtions.

        **Usage:**::

            @mqtt.unsubscribe()
            def handle_unsubscribe(client, userdata, mid)
                print('Unsubscribed from topic (id: {})'
                      .format(mid)')
        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_unsubscribe = handler
            return handler

        return decorator

    def on_log(self):
        # type: () -> Callable
        """Decorate a callback function to handle MQTT logging.

        **Example Usage:**

        ::

            @mqtt.on_log()
            def handle_logging(client, userdata, level, buf):
                print(client, userdata, level, buf)
        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_log = handler
            return handler

        return decorator
class Mqtt():
    def __init__(self, app=None):
        # type: (Flask) -> None
        self.app = app
        self.client = Client()
        self.client.on_connect = self._handle_connect
        self.client.on_disconnect = self._handle_disconnect
        self.topics = []  # type: List[str]
        self.connected = False

        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        # type: (Flask) -> None
        self.username = app.config.get('MQTT_USERNAME')
        self.password = app.config.get('MQTT_PASSWORD')
        self.broker_url = app.config.get('MQTT_BROKER_URL', 'localhost')
        self.broker_port = app.config.get('MQTT_BROKER_PORT', 1883)
        self.tls_enabled = app.config.get('MQTT_TLS_ENABLED', False)
        self.keepalive = app.config.get('MQTT_KEEPALIVE', 60)
        self.last_will_topic = app.config.get('MQTT_LAST_WILL_TOPIC')
        self.last_will_message = app.config.get('MQTT_LAST_WILL_MESSAGE')
        self.last_will_qos = app.config.get('MQTT_LAST_WILL_QOS', 0)
        self.last_will_retain = app.config.get('MQTT_LAST_WILL_RETAIN', False)

        if self.tls_enabled:
            self.tls_ca_certs = app.config['MQTT_TLS_CA_CERTS']
            self.tls_certfile = app.config.get('MQTT_TLS_CERTFILE')
            self.tls_keyfile = app.config.get('MQTT_TLS_KEYFILE')
            self.tls_cert_reqs = app.config.get('MQTT_TLS_CERT_REQS',
                                                ssl.CERT_REQUIRED)
            self.tls_version = app.config.get('MQTT_TLS_VERSION',
                                              ssl.PROTOCOL_TLSv1)
            self.tls_ciphers = app.config.get('MQTT_TLS_CIPHERS')
            self.tls_insecure = app.config.get('MQTT_TLS_INSECURE', False)

        # set last will message
        if self.last_will_topic is not None:
            self.client.will_set(self.last_will_topic, self.last_will_message,
                                 self.last_will_qos, self.last_will_retain)
        self.app = app
        self._connect()

    def _connect(self):
        # type: () -> None

        if self.username is not None:
            self.client.username_pw_set(self.username, self.password)

        # security
        if self.tls_enabled:
            if self.tls_insecure:
                self.client.tls_insecure_set(self.tls_insecure)

            self.client.tls_set(
                ca_certs=self.tls_ca_certs,
                certfile=self.tls_certfile,
                keyfile=self.tls_keyfile,
                cert_reqs=self.tls_cert_reqs,
                tls_version=self.tls_version,
                ciphers=self.tls_ciphers,
            )

        self.client.loop_start()
        res = self.client.connect(self.broker_url,
                                  self.broker_port,
                                  keepalive=self.keepalive)

    def _disconnect(self):
        # type: () -> None
        self.client.loop_stop()
        self.client.disconnect()

    def _handle_connect(self, client, userdata, flags, rc):
        # type: (Client, Any, Dict, int) -> None
        if rc == MQTT_ERR_SUCCESS:
            self.connected = True
            for topic in self.topics:
                self.client.subscribe(topic)

    def _handle_disconnect(self, client, userdata, rc):
        # type: (str, Any, int) -> None
        self.connected = False

    def on_topic(self, topic):
        # type: (str) -> Callable
        """
        Decorator to add a callback function that is called when a certain
        topic has been published. The callback function is expected to have the
        following form: `handle_topic(client, userdata, message)`

        :parameter topic: a string specifying the subscription topic to subscribe to

        The topic still needs to be subscribed via mqtt.subscribe() before the
        callback function can be used to handle a certain topic. This way it is
        possible to subscribe and unsubscribe during runtime.

        **Example usage:**::

            app = Flask(__name__)
            mqtt = Mqtt(app)
            mqtt.subscribe('home/mytopic')

            @mqtt.on_topic('home/mytopic')
            def handle_mytopic(client, userdata, message):
                print('Received message on topic {}: {}'
                      .format(message.topic, message.payload.decode()))

        """
        def decorator(handler):
            # type: (Callable[[str], None]) -> Callable[[str], None]
            self.client.message_callback_add(topic, handler)
            return handler

        return decorator

    def subscribe(self, topic, qos=0):
        # type: (str, int) -> tuple(int, int)
        """
        Subscribe to a certain topic.

        :param topic: a string specifying the subscription topic to subscribe to.
        :param qos: the desired quality of service level for the subscription.
                    Defaults to 0.

        :rtype: (int, int)
        :result: (result, mid)

        A topic is a UTF-8 string, which is used by the broker to filter
        messages for each connected client. A topic consists of one or more
        topic levels. Each topic level is separated by a forward slash
        (topic level separator).

        The function returns a tuple (result, mid), where result is
        MQTT_ERR_SUCCESS to indicate success or (MQTT_ERR_NO_CONN, None) if the
        client is not currently connected.  mid is the message ID for the
        subscribe request. The mid value can be used to track the subscribe
        request by checking against the mid argument in the on_subscribe()
        callback if it is defined.

        **Topic example:** `myhome/groundfloor/livingroom/temperature`

        """
        # TODO: add support for list of topics
        # don't subscribe if already subscribed
        # try to subscribe
        result, mid = self.client.subscribe(topic, qos)

        # if successful add to topics
        if result == MQTT_ERR_SUCCESS:
            if topic not in self.topics:
                self.topics.append(topic)

        return (result, mid)

    def unsubscribe(self, topic):
        # type: (str) -> tuple(int, int)
        """
        Unsubscribe from a single topic.

        :param topic: a single string that is the subscription topic to
                      unsubscribe from

        :rtype: (int, int)
        :result: (result, mid) 

        Returns a tuple (result, mid), where result is MQTT_ERR_SUCCESS
        to indicate success or (MQTT_ERR_NO_CONN, None) if the client is not
        currently connected.
        mid is the message ID for the unsubscribe request. The mid value can be
        used to track the unsubscribe request by checking against the mid
        argument in the on_unsubscribe() callback if it is defined.

        """
        # don't unsubscribe if not in topics
        if topic not in self.topics:
            return

        result, mid = self.client.unsubscribe(topic)

        # if successful remove from topics
        if result == MQTT_ERR_SUCCESS:
            self.topics.remove(topic)

        return result, mid

    def unsubscribe_all(self):
        # type: () -> None
        """
        Unsubscribe from all topics.

        """
        topics = self.topics[:]
        for topic in topics:
            self.unsubscribe(topic)

    def publish(self, topic, payload=None, qos=0, retain=False):
        # type: (str, bytes, int, bool) -> Tuple[int, int]
        """
        Send a message to the broker.

        :param topic: the topic that the message should be published on
        :param payload: the actual message to send. If not given, or set to
                        None a zero length message will be used. Passing an
                        int or float will result in the payload being
                        converted to a string representing that number.
                        If you wish to send a true int/float, use struct.pack()
                        to create the payload you require.
        :param qos: the quality of service level to use
        :param retain: if set to True, the message will be set as the
                       "last known good"/retained message for the topic

        :returns: Returns a tuple (result, mid), where result is
                  MQTT_ERR_SUCCESS to indicate success or MQTT_ERR_NO_CONN
                  if the client is not currently connected. mid is the message
                  ID for the publish request.

        """
        if not self.connected:
            self.client.reconnect()
        return self.client.publish(topic, payload, qos, retain)

    def on_message(self):
        # type: () -> Callable
        """
        Decorator to handle all messages that have been subscribed and that
        are not handled via the `on_message` decorator.

        **Note:** Unlike as written in the paho mqtt documentation this 
        callback will not be called if there exists an topic-specific callback
        added by the `on_topic` decorator.

        **Example Usage:**::

            @mqtt.on_message()
            def handle_messages(client, userdata, message):
                print('Received message on topic {}: {}'
                      .format(message.topic, message.payload.decode()))

        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_message = handler
            return handler

        return decorator

    def on_publish(self):
        """
        Decorator to handle all messages that have been published by the
        client.

        **Example Usage:**::

            @mqtt.on_publish()
            def handle_publish(client, userdata, mid):
                print('Published message with mid {}.'
                      .format(mid))
        """
        def decorator(handler):
            self.client.on_publish = handler
            return handler

        return decorator

    def on_subscribe(self):
        """
        Decorator to handle subscribe callbacks.

        **Usage:**::

            @mqtt.on_subscribe()
            def handle_subscribe(client, userdata, mid, granted_qos):
                print('Subscription id {} granted with qos {}.'
                      .format(mid, granted_qos))

        """
        def decorator(handler):
            self.client.on_subscribe = handler
            return handler

        return decorator

    def on_unsubscribe(self):
        """
        Decorator to handle unsubscribe callbacks.

        **Usage:**::

            @mqtt.unsubscribe()
            def handle_unsubscribe(client, userdata, mid)
                print('Unsubscribed from topic (id: {})'
                      .format(mid)')
                
        """
        def decorator(handler):
            self.client.on_unsubscribe = handler
            return handler

        return decorator

    def on_log(self):
        # type: () -> Callable
        """
        Decorator to handle MQTT logging.

        **Example Usage:**

        ::
            
            @mqtt.on_log()
            def handle_logging(client, userdata, level, buf):
                print(client, userdata, level, buf)

        """
        def decorator(handler):
            # type: (Callable) -> Callable
            self.client.on_log = handler
            return handler

        return decorator
Example #15
0
 def subscribe(self, client: MQTTClient.Client):
     logger.info("Subscribing for topic '{}'".format(self.sub_topic))
     client.subscribe(self.sub_topic)
     client.message_callback_add(self.sub_topic, self.on_message)
Example #16
0
class RhasspyMQTTClient:
    def __init__(self,
                 host="",
                 port=1883,
                 username="",
                 password="",
                 tls=False,
                 cacerts=None,
                 recording=False,
                 jsonfolder="",
                 logger=None):
        """The __init__ function of custom MQTT Class.
        Args:
            host (str)               : MQTT Server name or IP.
            port (int)               : MQTT server TCP port.
            logger (class:logging.Logger): Logger object for logging messages.
            username (str)(option)   : User name to connect MQTT server.
            password (str)(option)   : Password to connect MQTT server.
            tls (bool)               : Use TLS to connect to MQTT server.
            cacerts (str)            : CA path to verify the MQTT server's TLS certificate, or None for the system's default CA system.
            recording (bool)          : save all messages as json file (and wave).
            jsonfolder (str)         : Folder where messages are saved
        """
        ## Properties
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.tls = tls
        self.cacerts = cacerts
        self.recording = recording
        self.jsonfolder = jsonfolder
        self.logger = logger
        self.__dateFileFormat = '%Y%m%d%H%M%S%f'

        ## Dict who contains pair key/value by site
        ## Key is site name / Value is array of wave bytes received
        self.__audioFrames = {}
        self.__playBytes = {}

        ## Paho Mqtt Client
        self.__mqtt = Client()
        self.__mqtt.message_callback_add("hermes/audioServer/#", self.on_audio)

        ## override some methods
        self.__mqtt.on_connect = self.on_cnx  ### GRRRR
        self.__mqtt.on_message = self.on_msg

    def __saveJson(self, payload, topic, logTime):
        """Save each MQTT message (not audio) as a json file """

        self.logger.debug('enter in show_message method.')

        strFileTime = logTime.strftime(self.__dateFileFormat)

        ## Get payloada nd save topic info in json
        payload.update({"topic": topic})

        ## Dump the payload to json file
        with open(os.path.join(self.jsonfolder, strFileTime + ".json"),
                  'w') as outfile:
            json.dump(payload, outfile)

        self.logger.debug("payload saved in %s.json file", strFileTime)

    def __saveWave(self, siteId, logTime, arrBytesWav, flux):
        """save all audio messages as wave files """

        self.logger.debug('enter in __saveWave private method.')

        strFileTime = logTime.strftime(self.__dateFileFormat)

        ## Generate the wave file name with name, siteId and flux
        wave_filename = os.path.join(
            self.jsonfolder, strFileTime + "_" + siteId + "_" + flux + ".wav")
        self.logger.debug("Saving array of waves in %s.wav ", strFileTime)

        ## open the wave file with write access
        output = wave.open(wave_filename, 'wb')

        try:
            ## set wave file parameters from first wave element in array
            with io.BytesIO(arrBytesWav[0]) as wav_buffer:
                with wave.open(wav_buffer, 'rb') as w:
                    output.setparams(w.getparams())

            try:
                ## Extract each frames from all wave elements in arrBytesWav
                ## to add them to output wave file
                for wav in arrBytesWav:
                    with io.BytesIO(wav) as wav_buffer:
                        with wave.open(wav_buffer, 'rb') as w:
                            output.writeframes(w.readframes(w.getnframes()))

                output.close()

                self.logger.debug("%s.wav saved successfully ", strFileTime)

                self.on_saved_wav(
                    strFileTime + "_" + siteId + "_" + flux + ".wav", siteId,
                    flux, logTime)
            except:
                self.logger.warning(
                    "ERROR : Failed to extract frames from array", strFileTime)
                output.close()
        except:
            self.logger.warning(
                "ERROR : Failed to get wave param in first frames ",
                strFileTime)
            output.close()

    def connect(self):
        """Connect to the MQTT broker defined in the configuration. """

        self.logger.debug('enter in connect method.')

        if self.username != "":
            self.logger.debug('Setting username and password for MQTT broker.')
            self.__mqtt.username_pw_set(self.username, self.password)

        if self.tls:
            self.logger.debug('Setting TLS for MQTT broker.')
            self.__mqtt.tls_set(ca_certs=self.cacerts)

        self.logger.info('Connecting to MQTT broker %s:%s...', str(self.host),
                         str(self.port))

        self.__mqtt.connect(self.host, self.port)
        self.__mqtt.loop_forever()

    def translate_message(self, payload, topic, strLogTime, outputFormat):
        """ 2 possibilities (actually) for output text
            - In human readable text
            - In json text format with dump of payload

            This method returns text in desired format
        """

        self.logger.debug('enter in translate_message method.')

        if (outputFormat == "raw"):
            text = "{0}".format(payload)
            logText = "[{0}] {1} - {2}".format(strLogTime, topic, text)
        else:
            text = self.get_humanText(payload, topic)
            logText = "[{0}] {1}".format(strLogTime, text)

        return logText

    def show_message(self, text, outputFile, noStandardOut):
        """ this methods is used to manage the MQTT message display
            - noStandardOut : If true, nothing write to stdout
            - outputFile    : If not empty, all MQTT message will be
                              saved inside this file
        """
        self.logger.debug('enter in show_message method.')

        if not noStandardOut:
            print(text)

        if outputFile != "":
            with codecs.open(outputFile, 'a', encoding='utf8') as f:
                f.write(text + "\n")
                f.close()

    def get_humanText(self, payload, topic):
        """ This method translate Rhasspy/snips MQTT message as
            human readable text
            Maybe all topics are not processed. 
        """

        self.logger.debug('enter in get_humanText method.')
        self.logger.debug('Topic : {0}'.format(topic))

        ########################
        #       HOTWORD        #
        ########################
        if "hermes/hotword/toggleOn" in topic:
            text = colored("[hotword]",'magenta') + \
                " was asked to toggle itself 'on' on site {0}"\
                .format (colored(payload['siteId'],'white',attrs=['bold']))

        elif "hermes/hotword/toggleOff" in topic:
            text = colored("[hotword]",'magenta') + \
                " was asked to toggle itself 'off' on site {0}"\
                .format (colored(payload['siteId'],'white',attrs=['bold']))

        elif ("hermes/hotword/" in topic) and ("/detected" in topic):
            text = colored("[hotword]",'yellow') + \
                " detected on site {0}, for model {1}"\
                .format(colored(payload['siteId'],'white',attrs=['bold']),
                        payload['modelId'])

        ########################
        #         ASR          #
        ########################
        elif "hermes/asr/stopListening" in topic:
            text = colored("[Asr]",'magenta') + \
                " was asked to stop listening on site {0}"\
                .format (colored(payload['siteId'],'white',attrs=['bold']))

        elif ("hermes/asr/startListening" in topic):
            text = colored("[Asr]",'magenta') + \
                " was asked to listen on site {0}"\
                .format (colored(payload['siteId'],'white',attrs=['bold']))

        elif ("hermes/asr/textCaptured" in topic):
            text = colored("[Asr]",'yellow') + \
                " captured text '{0}' in {1}s on site {2}"\
                .format(colored(payload['text'],'green',attrs=['bold']),
                        payload['seconds'],
                        colored(payload['siteId'],'white',attrs=['bold']))

        elif "hermes/asr/toggleOn" in topic:
            text = colored("[Asr]",'magenta') + \
                " was asked to toggle itself 'on' on site {0}"\
                .format (colored(payload['siteId'],'white',attrs=['bold']))

        elif "hermes/asr/toggleOff" in topic:
            text = colored("[Asr]",'magenta') + \
                " was asked to toggle itself 'off' on site {0}"\
                .format (colored(payload['siteId'],'white',attrs=['bold']))

        ########################
        #   DIALOGUE MANAGER   #
        ########################
        elif ("hermes/dialogueManager/sessionStarted" in topic):
            text = colored("[Dialogue]",'yellow') \
                + " session with id {0} was started on site {1}."\
                .format(payload['sessionId'],
                        colored(payload['siteId'],'white',attrs=['bold']))

        elif ("hermes/dialogueManager/sessionEnded" in topic):
            text = colored("[Dialogue]",'yellow') + \
                " session with id {0} was ended on site {1}. Reason: {2}"\
                .format(payload['sessionId'],
                        colored(payload['siteId'],'white',attrs=['bold']),
                        payload['termination']['reason'])
            if 'customData' in payload.keys():
                if payload['customData'] is not None:
                    text = text + "\n           with customData : "
                    text = text + "\n               {0} "\
                        .format(colored(payload['customData'],'cyan', attrs=['bold']))

        elif ("hermes/dialogueManager/endSession" in topic):
            text = colored("[Dialogue]",'magenta') + \
                " was ask to end session with id {0} by saying '{1}'"\
                .format(payload['sessionId'],
                        (payload['text']))

        elif ("hermes/dialogueManager/continueSession" in topic):
            text = colored("[Dialogue]",'magenta') + \
                " was ask to continue session with id {0} by saying '{1}'"\
                .format(payload['sessionId'],
                        (payload['text']))
            if 'customData' in payload.keys():
                if payload['customData'] is not None:
                    text = text + "\n           with customData : "
                    text = text + "\n               {0}"\
                            .format(colored(payload['customData'],'cyan', attrs=['bold']))

        elif ("hermes/dialogueManager/intentNotRecognized" in topic):
            text = colored("[Dialogue]",'red') + \
                " Intent NOT recognized for session with id {0} by saying '{1}'"\
                .format(payload['sessionId'],
                        (payload['input']))
            if 'customData' in payload.keys():
                if payload['customData'] is not None:
                    text = text + "\n           with customData : "
                    text = text + "\n               {0}"\
                            .format(colored(payload['customData'],'cyan', attrs=['bold']))

        ########################
        #         NLU          #
        ########################
        elif ("hermes/nlu/query" in topic):
            text = colored("[Nlu]",'magenta') + \
                " was asked to parse input '{0}'"\
                .format(payload['input'])

        elif ("hermes/nlu/intentNotRecognized" in topic):
            text = colored("[Nlu]",'yellow') + \
                " Intent not recognized for {0}"\
                .format(colored(payload['input'],'red', attrs=['bold']))

        elif ("hermes/nlu/intentParsed" in topic):
            text = colored("[Nlu]",'yellow') + \
                " Detected intent {0} with confidence score {1} for input '{2}'"\
                .format(colored(payload["intent"]["intentName"],'green', attrs=['bold']),
                        payload["intent"]["confidenceScore"],
                        payload['input'])

        ########################
        #       INTENT         #
        ########################
        elif ("hermes/intent/" in topic):

            text = colored("[Nlu]",'yellow') + \
                " Intent {0} with confidence score {1} on site {2} "\
                .format(colored(payload["intent"]["intentName"],'green', attrs=['bold']),
                        payload["intent"]["confidenceScore"],
                        colored(payload['siteId'],'white',attrs=['bold']))
            """
            In snips, in slot, the word is "confidenceScore"
            In rhasspy, in slot, the word is "confidence"

            """
            if len(payload['slots']) > 0:

                text = text + "\n           with slots : "
                for slot in payload['slots']:

                    confidence = "N/A"
                    if "confidenceScore" in slot.keys():
                        confidence = slot['confidenceScore']
                    else:
                        confidence = slot['confidence']

                    text = text + "\n               {0} => {1} (confidenceScore={2})"\
                        .format(colored(slot['slotName'],'cyan', attrs=['bold']),
                                slot['value']['value'],
                                confidence)

            if 'customData' in payload.keys():
                if payload['customData'] is not None:
                    text = text + "\n           with customData : "
                    text = text + "\n               {0} "\
                        .format(colored(payload['customData'],'cyan', attrs=['bold']))

        ########################
        #         TTS          #
        ########################
        elif ("hermes/tts/say" == topic):
            text = colored("[Tts]",'yellow') + \
                " was asked to say '{0}' in {1} on site {2}".\
                format(colored(payload['text'],'green', attrs=['bold']),
                    payload['lang'],
                    colored(payload['siteId'],'white',attrs=['bold']))

        elif ("hermes/tts/sayFinished" in topic):
            text = colored("[Tts]",'cyan') + \
                " finished speaking with id '{0}'"\
                .format(payload['sessionId'])

        ########################
        #    AUDIO SERVER      #
        ########################
        elif ("hermes/audioServer" in topic):
            text = colored("[audioServer]",'cyan') + \
                " audio on topic {0}".format(topic)

        ########################
        #       UNKNOWN        #
        ########################
        else:
            self.logger.warning('Unknow topic : {0}'.format(topic))
            text = colored("[UNKNOWN]",'red') + \
                " message on topic {0}".format(topic)

        return text

    def search_message(self, datestart, datestop, siteId, jsonfolder,
                       searchoutputFormat, outputFile):
        """ This method allow to query all MQTT messages saved as file """

        self.logger.debug('enter in search_message method.')

        ## Get list of all json files sorted by name (so by datetime)
        allJsonFiles = os.listdir(jsonfolder)
        allJsonFiles.sort()

        ## For each file, check if it's between the start and stop search date
        for filename in allJsonFiles:

            ## Get extension and name of file
            filenameWithoutExt, extension = (
                os.path.basename(filename)).split(".")

            self.logger.debug('filename %s - ext %s', filenameWithoutExt,
                              extension)

            if extension == "wav":
                """ If extension is .wav, so in name, there are :
                    myDate = date time when the wav was saved
                    siteId = The site of Rhasspy/snips
                    flux   = if it's input or output wave file
                        input  : play on the siteId
                        output : record from the siteId

                    Ex wave filename : 20200429195055646804_bureau_play.wav
                """

                ## Get date, siteId, flux
                strDate, siteId, flux = filenameWithoutExt.split("_")
                myDate = datetime.strptime(strDate, self.__dateFileFormat)

                ## We check if file date is between start and stop search date
                if datestart <= myDate <= datestop:
                    self.logger.debug(
                        'WAV : strDate : %s - siteId : %s - flux : %s',
                        strDate, siteId, flux)

                    ## If yes, call the on_saved_wav
                    self.on_saved_wav(filename, siteId, flux, myDate)

            elif extension == "json":
                ## Get date
                strDate = filenameWithoutExt.split("_")[0]
                myDate = datetime.strptime(strDate, self.__dateFileFormat)

                ## As script knows the format, it can retrieve the date as datetime type
                myDate = datetime.strptime(filenameWithoutExt,
                                           self.__dateFileFormat)

                ## We check if file date is between start and stop date search
                if datestart <= myDate <= datestop:

                    ## The file is concerned by search. Script open it and load json
                    with open(os.path.join(jsonfolder, filename)) as json_file:
                        payload = json.load(json_file)

                    ## Remove the 'topic' json element imported when json file was saved
                    ## the 'topic' element is added to file only to retieve information
                    topic = bytes(payload['topic'], 'utf-8')
                    payload.pop('topic', None)

                    ## Create a paho MQTT message
                    myMQTTmessage = MQTTMessage(mid=0, topic=topic)
                    myMQTTmessage.payload = json.dumps(payload).encode('utf-8')

                    ## call on_message method and pass the MQTT message
                    self.on_message(None, None, myMQTTmessage, myDate)

    def on_audio(self, client, userdata, msg):
        """ Specific method to intercept audio MQTT message and save data
        in a array of bytearray.
        The entire array will be dump in a wav file on specific MQTT message """
        self.logger.debug('enter in on_audio method. (read audio stream)')

        currentTime = datetime.now()

        if self.recording:
            siteId = ((msg.topic).split("/"))[2]
            flux = ((msg.topic).split("/"))[3]

            ## If it's record stream
            if flux == "audioFrame":
                if siteId not in self.__audioFrames:
                    self.__audioFrames[siteId] = []

                ## Add this piece of wave in an array of bytes
                (self.__audioFrames[siteId]).append(bytearray(msg.payload))

            ## If it's a play stream
            if flux == "playBytesStreaming":
                if siteId not in self.__playBytes:
                    self.__playBytes[siteId] = []

                (self.__playBytes[siteId]).append(bytearray(msg.payload))

            ## when topic contains "playBytes" or "streamFinished", it means
            ## play stream has stopped on rhasspy. So the wav file can be saved.
            ## Use <IS_LAST_CHUNK> instead ?
            if (flux == "playBytes") or (flux == "streamFinished"):
                self.logger.debug("fin streaming on site %s", siteId)
                if (siteId not in self.__playBytes) or (len(
                        self.__playBytes[siteId]) == 0):
                    self.__playBytes[siteId] = []
                    (self.__playBytes[siteId]).append(bytearray(msg.payload))

                count_item = len(self.__playBytes[siteId])
                self.logger.debug("count of item in array %s", str(count_item))

                if len(self.__playBytes[siteId]) > 0:
                    self.__saveWave(siteId, currentTime,
                                    self.__playBytes[siteId], 'play')
                    self.__playBytes[siteId] = []

    def on_msg(self, client, userdata, msg):
        """The on_message callback of paho MQTT client is intercepted by this on_msg method 
        before the propagation of MQTT message.
        Here, we can :
            - Save payload to json if porperty recording is True
            - Save output wave file when specific message is received
            - Save input wave file when specific message is received
            - propagate the MQTT message to on_message method of our custom MQTT class

        """
        self.logger.debug('enter in on_msg method.')

        currentTime = datetime.now()

        ## If MQTT message has to be saved in file
        if self.recording:

            ## If message does not come from audioServer
            ## the message is saved as json file
            if "hermes/audioServer/" not in msg.topic:
                payload = json.loads(msg.payload.decode('utf8'))
                self.__saveJson(payload, msg.topic, currentTime)

            ## If "textCaptured" is in topic, it means ASR stop
            ## to record from Rhasspy. So the wav file can be saved.
            if "hermes/asr/textCaptured" in msg.topic:
                self.__saveWave(payload['siteId'], currentTime,
                                self.__audioFrames[payload['siteId']],
                                'record')
                self.__audioFrames[payload['siteId']] = []

        ## Propagate the message MQTT
        self.on_message(client, userdata, msg, currentTime)

    ## Not good enough in python to avoid this :/
    def on_cnx(self, client, userdata, flags, result_code):
        self.on_connect(client=client,
                        userdata=userdata,
                        flags=flags,
                        result_code=result_code)

    def subscribe(self, topic):
        """Method used to subscribe to private MQTT object topic """
        self.__mqtt.subscribe(topic)

    def on_message(self, client, userdata, msg, logTime):
        """Event method """

    def on_connect(self, client, userdata, flags, result_code):
        """Event method """

    def on_saved_wav(self, filename, siteId, flux, logTime):
        """Event method """
Example #17
0
class LampiApp(App):
    _updated = False
    _updatingUI = False
    _hue = NumericProperty()
    _saturation = NumericProperty()
    _brightness = NumericProperty()
    lamp_is_on = BooleanProperty()

    def _get_hue(self):
        return self._hue

    def _set_hue(self, value):
        self._hue = value

    def _get_saturation(self):
        return self._saturation

    def _set_saturation(self, value):
        self._saturation = value

    def _get_brightness(self):
        return self._brightness

    def _set_brightness(self, value):
        self._brightness = value

    hue = AliasProperty(_get_hue, _set_hue, bind=['_hue'])
    saturation = AliasProperty(_get_saturation,
                               _set_saturation,
                               bind=['_saturation'])
    brightness = AliasProperty(_get_brightness,
                               _set_brightness,
                               bind=['_brightness'])
    gpio17_pressed = BooleanProperty(False)
    device_associated = BooleanProperty(True)

    def on_start(self):
        self.keen = KeenEventRecorder(PROJECT_ID, WRITE_KEY, get_device_id())
        self._publish_clock = None
        self.mqtt_broker_bridged = False
        self._associated = True
        self.association_code = None
        self.mqtt = Client(client_id=MQTT_CLIENT_ID)
        self.mqtt.will_set(client_state_topic(MQTT_CLIENT_ID),
                           "0",
                           qos=2,
                           retain=True)
        self.mqtt.on_connect = self.on_connect
        self.mqtt.connect(MQTT_BROKER_HOST,
                          port=MQTT_BROKER_PORT,
                          keepalive=MQTT_BROKER_KEEP_ALIVE_SECS)
        self.mqtt.loop_start()
        self.set_up_GPIO_and_device_status_popup()
        self.associated_status_popup = self._build_associated_status_popup()
        self.associated_status_popup.bind(on_open=self.update_popup_associated)
        Clock.schedule_interval(self._poll_associated, 0.1)

    def _build_associated_status_popup(self):
        return Popup(title='Associate your Lamp',
                     content=Label(text='Msg here', font_size='30sp'),
                     size_hint=(1, 1),
                     auto_dismiss=False)

    def on_hue(self, instance, value):
        if self._updatingUI:
            return
        evt = self._create_event_record('hue-slider', value)
        self.keen.record_event('ui', evt)
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_saturation(self, instance, value):
        if self._updatingUI:
            return
        evt = self._create_event_record('saturation-slider', value)
        self.keen.record_event('ui', evt)
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_brightness(self, instance, value):
        if self._updatingUI:
            return
        evt = self._create_event_record('brightness-slider', value)
        self.keen.record_event('ui', evt)
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_lamp_is_on(self, instance, value):
        if self._updatingUI:
            return
        evt = self._create_event_record('power', value)
        self.keen.record_event('ui', evt)
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def _create_event_record(self, element, value):
        return {'element': {'id': element, 'value': value}}

    def on_connect(self, client, userdata, flags, rc):
        self.mqtt.publish(client_state_topic(MQTT_CLIENT_ID),
                          "1",
                          qos=2,
                          retain=True)
        self.mqtt.message_callback_add(TOPIC_LAMP_CHANGE_NOTIFICATION,
                                       self.receive_new_lamp_state)
        self.mqtt.message_callback_add(broker_bridge_connection_topic(),
                                       self.receive_bridge_connection_status)
        self.mqtt.message_callback_add(TOPIC_LAMP_ASSOCIATED,
                                       self.receive_associated)
        self.mqtt.subscribe(broker_bridge_connection_topic(), qos=1)
        self.mqtt.subscribe(TOPIC_LAMP_CHANGE_NOTIFICATION, qos=1)
        self.mqtt.subscribe(TOPIC_LAMP_ASSOCIATED, qos=2)

    def _poll_associated(self, dt):
        # this polling loop allows us to synchronize changes from the
        #  MQTT callbacks (which happen in a different thread) to the
        #  Kivy UI
        self.device_associated = self._associated

    def receive_associated(self, client, userdata, message):
        # this is called in MQTT event loop thread
        new_associated = json.loads(message.payload)
        if self._associated != new_associated['associated']:
            if not new_associated['associated']:
                self.association_code = new_associated['code']
            else:
                self.association_code = None
            self._associated = new_associated['associated']

    def on_device_associated(self, instance, value):
        if value:
            self.associated_status_popup.dismiss()
        else:
            self.associated_status_popup.open()

    def update_popup_associated(self, instance):
        code = self.association_code[0:6]
        instance.content.text = ("Please use the\n"
                                 "following code\n"
                                 "to associate\n"
                                 "your device\n"
                                 "on the Web\n{}".format(code))

    def receive_bridge_connection_status(self, client, userdata, message):
        # monitor if the MQTT bridge to our cloud broker is up
        if message.payload == "1":
            self.mqtt_broker_bridged = True
        else:
            self.mqtt_broker_bridged = False

    def receive_new_lamp_state(self, client, userdata, message):
        new_state = json.loads(message.payload)
        Clock.schedule_once(lambda dt: self._update_ui(new_state), 0.01)

    def _update_ui(self, new_state):
        if self._updated and new_state['client'] == MQTT_CLIENT_ID:
            # ignore updates generated by this client, except the first to
            #   make sure the UI is syncrhonized with the lamp_service
            return
        self._updatingUI = True
        try:
            if 'color' in new_state:
                self.hue = new_state['color']['h']
                self.saturation = new_state['color']['s']
            if 'brightness' in new_state:
                self.brightness = new_state['brightness']
            if 'on' in new_state:
                self.lamp_is_on = new_state['on']
        finally:
            self._updatingUI = False

        self._updated = True

    def _update_leds(self):
        msg = {
            'color': {
                'h': self._hue,
                's': self._saturation
            },
            'brightness': self._brightness,
            'on': self.lamp_is_on,
            'client': MQTT_CLIENT_ID
        }
        self.mqtt.publish(TOPIC_SET_LAMP_CONFIG, json.dumps(msg), qos=1)
        self._publish_clock = None

    def set_up_GPIO_and_device_status_popup(self):
        self.pi = pigpio.pi()
        self.pi.set_mode(17, pigpio.INPUT)
        self.pi.set_pull_up_down(17, pigpio.PUD_UP)
        Clock.schedule_interval(self._poll_GPIO, 0.05)
        self.network_status_popup = self._build_network_status_popup()
        self.network_status_popup.bind(on_open=self.update_device_status_popup)

    def _build_network_status_popup(self):
        return Popup(title='Device Status',
                     content=Label(text='IP ADDRESS WILL GO HERE'),
                     size_hint=(1, 1),
                     auto_dismiss=False)

    def update_device_status_popup(self, instance):
        interface = "wlan0"
        ipaddr = lampi_util.get_ip_address(interface)
        deviceid = lampi_util.get_device_id()
        msg = ("Version: {}\n"
               "{}: {}\n"
               "DeviceID: {}\n"
               "Broker Bridged: {}\n"
               "threaded").format(LAMPI_APP_VERSION, interface, ipaddr,
                                  deviceid, self.mqtt_broker_bridged)
        instance.content.text = msg

    def on_gpio17_pressed(self, instance, value):
        if value:
            self.network_status_popup.open()
        else:
            self.network_status_popup.dismiss()

    def _poll_GPIO(self, dt):
        # GPIO17 is the rightmost button when looking front of LAMPI
        self.gpio17_pressed = not self.pi.read(17)
Example #18
0
class MqttClient():
    default_config = {
        "host": "api.raptorbox.eu",
        "port": 1883,
        "reconnect_min_delay": 1,
        "reconnect_max_delay": 127,
    }

    def __init__(self, config):
        '''
        Initialize information for the client

        provide configurations as parameter, if None provided the configurations would be defaults

        see MqttClient.default_config for configurations format
        '''
        self.config = MqttClient.default_config
        if config:
            for k, v in config.iteritems():
                self.config[k] = v

        self.mqtt_client = Client()
        self.mqtt_client.username_pw_set(config["username"],
                                         config["password"])

        if "reconnect_min_delay" in config and "reconnect_max_delay" in config:
            self.mqtt_client.reconnect_delay_set(
                config["reconnect_min_delay"],
                config["reconnect_max_delay"],
            )
        # self.connection_thread = None

        # self.mqtt_client.tls_set()

    def connect(self):  #TODO consider srv field in dns
        '''
        connect the client to mqtt server with configurations provided by constructor
        and starts listening for topics
        '''
        self.mqtt_client.connect(self.config["host"], self.config["port"])

        # self.mqtt_client.loop_forever()
        self.mqtt_client.loop_start()

    def subscribe(self, topic, callback):
        '''
        register 'callback' to "topic". Every message will be passed to callback in order to be executed
        '''
        logging.debug("subscribing to topic: {}".format(topic))
        self.mqtt_client.subscribe(topic)
        self.mqtt_client.message_callback_add(topic, callback)

    def unsubscribe(self, topic):
        '''
        unsubscribe to topic

        topic could be either a string or al list of strings containing to topics to unsubscribe to.

        Returns a tuple (result, mid), where result is MQTT_ERR_SUCCESS
        to indicate success or (MQTT_ERR_NO_CONN, None) if the client is not
        currently connected.
        mid is the message ID for the unsubscribe request. The mid value can be
        used to track the unsubscribe request by checking against the mid
        argument in the on_unsubscribe() callback if it is defined.
        '''
        return self.mqtt_client.unsubscribe(topic)

    def disconnect(self):
        '''
        Disconnects from the server
        '''
        self.mqtt_client.disconnect()

    def reconnect(self):
        '''
        Reconnects after a disconnection, this could be called after connection only
        '''
        self.mqtt_client.reconnect()
Example #19
0
class MQTTConnection():
    client: Client
    _instance = None

    @classmethod
    def get_instance(cls) -> "MQTTConnection":
        if cls._instance is None:
            cls._instance = MQTTConnection()

        return cls._instance

    def __init__(self):
        self.client = Client(
            "pai" + os.urandom(8).hex(),
            protocol=protocol_map.get(str(cfg.MQTT_PROTOCOL), MQTTv311),
            transport=cfg.MQTT_TRANSPORT,
        )
        self._last_pai_status = "unknown"
        self.pai_status_topic = "{}/{}/{}".format(cfg.MQTT_BASE_TOPIC,
                                                  cfg.MQTT_INTERFACE_TOPIC,
                                                  "pai_status")
        self.availability_topic = "{}/{}/{}".format(cfg.MQTT_BASE_TOPIC,
                                                    cfg.MQTT_INTERFACE_TOPIC,
                                                    "availability")
        self.client.on_connect = self._on_connect_cb
        self.client.on_disconnect = self._on_disconnect_cb
        self.state = ConnectionState.NEW
        # self.client.enable_logger(logger)

        # self.client.on_subscribe = lambda client, userdata, mid, granted_qos: logger.debug("Subscribed: %s" %(mid))
        # self.client.on_message = lambda client, userdata, message: logger.debug("Message received: %s" % str(message))
        # self.client.on_publish = lambda client, userdata, mid: logger.debug("Message published: %s" % str(mid))

        ps.subscribe(self.on_run_state_change, "run-state")

        self.registrars = []

        if cfg.MQTT_USERNAME is not None and cfg.MQTT_PASSWORD is not None:
            self.client.username_pw_set(username=cfg.MQTT_USERNAME,
                                        password=cfg.MQTT_PASSWORD)

        if cfg.MQTT_TLS_CERT_PATH is not None:
            self.client.tls_set(
                ca_certs=cfg.MQTT_TLS_CERT_PATH,
                certfile=None,
                keyfile=None,
                cert_reqs=ssl.CERT_REQUIRED,
                tls_version=ssl.PROTOCOL_TLSv1_2,
                ciphers=None,
            )
            self.client.tls_insecure_set(False)

        self.client.will_set(self.availability_topic,
                             "offline",
                             0,
                             retain=True)

        self.client.on_log = self.on_client_log

    def on_client_log(self, client, userdata, level, buf):
        level_std = LOGGING_LEVEL[level]
        exc_info = None

        type_, exc, trace = sys.exc_info()
        if exc:  # Can be (socket.error, OSError, WebsocketConnectionError, ...)
            if hasattr(exc, "errno"):
                exc_msg = f"{os.strerror(exc.errno)}({exc.errno})"
                if exc.errno in [22, 49]:
                    level_std = logging.ERROR
                    buf = f"{buf}: Please check MQTT connection settings. Especially MQTT_BIND_ADDRESS and MQTT_BIND_PORT"
            else:
                exc_msg = str(exc)

            buf = f"{buf}: {exc_msg}"
            if "Connection failed" in buf:
                level_std = logging.WARNING

        if level_std > logging.DEBUG:
            logger.log(level_std, buf, exc_info=exc_info)

    def on_run_state_change(self, state: RunState):
        v = RUN_STATE_2_PAYLOAD.get(state, "unknown")
        self._report_pai_status(v)

    def start(self):
        if self.state == ConnectionState.NEW:
            self.client.loop_start()

            # TODO: Some initial connection retry mechanism required
            try:
                self.client.connect_async(
                    host=cfg.MQTT_HOST,
                    port=cfg.MQTT_PORT,
                    keepalive=cfg.MQTT_KEEPALIVE,
                    bind_address=cfg.MQTT_BIND_ADDRESS,
                    bind_port=cfg.MQTT_BIND_PORT,
                )

                self.state = ConnectionState.CONNECTING

                logger.info("MQTT loop started")
            except socket.gaierror:
                logger.exception("Failed to connect to MQTT (%s:%d)",
                                 cfg.MQTT_HOST, cfg.MQTT_PORT)

    def stop(self):
        if self.state in [
                ConnectionState.CONNECTING, ConnectionState.CONNECTED
        ]:
            self.disconnect()
            self.client.loop_stop()
            logger.info("MQTT loop stopped")

    def publish(self, topic, payload=None, *args, **kwargs):
        logger.debug("MQTT: {}={}".format(topic, payload))

        self.client.publish(topic, payload, *args, **kwargs)

    def _call_registars(self, method, *args, **kwargs):
        for r in self.registrars:
            try:
                if hasattr(r, method) and isinstance(getattr(r, method),
                                                     typing.Callable):
                    getattr(r, method)(*args, **kwargs)
            except:
                logger.exception('Failed to call "%s" on "%s"', method,
                                 r.__class__.__name__)

    def register(self, cls):
        self.registrars.append(cls)

        self.start()

    def unregister(self, cls):
        self.registrars.remove(cls)

        if len(self.registrars) == 0:
            self.stop()

    @property
    def connected(self):
        return self.state == ConnectionState.CONNECTED

    def _report_pai_status(self, status):
        self._last_pai_status = status
        self.publish(self.pai_status_topic,
                     status,
                     qos=cfg.MQTT_QOS,
                     retain=True)
        self.publish(
            self.availability_topic,
            "online" if status in ["online", "paused"] else "offline",
            qos=cfg.MQTT_QOS,
            retain=True,
        )

    def _on_connect_cb(self, client, userdata, flags, result, properties=None):
        # called on Thread-6
        if result == MQTT_ERR_SUCCESS:
            logger.info("MQTT Broker Connected")
            self.state = ConnectionState.CONNECTED
            self._report_pai_status(self._last_pai_status)
            self._call_registars("on_connect", client, userdata, flags, result)
        else:
            logger.error(
                f"Failed to connect to MQTT: {connack_string(result)} ({result})"
            )

    def _on_disconnect_cb(self, client, userdata, rc):
        # called on Thread-6
        if rc == MQTT_ERR_SUCCESS:
            logger.info("MQTT Broker Disconnected")
        else:
            logger.error(f"MQTT Broker unexpectedly disconnected. Code: {rc}")

        self.state = ConnectionState.NEW
        self._call_registars("on_disconnect", client, userdata, rc)

    def disconnect(self, reasoncode=None, properties=None):
        self.state = ConnectionState.DISCONNECTING
        self._report_pai_status("offline")
        self.client.disconnect()

    def message_callback_add(self, *args, **kwargs):
        self.client.message_callback_add(*args, **kwargs)

    def subscribe(self, *args, **kwargs):
        self.client.subscribe(*args, **kwargs)
Example #20
0
class Bridge(object):
    WEBSOCKETS = "websocket"
    SSE = "sse"

    def __init__(self, args, ioloop, dynamic_subscriptions):
        """ parse config values and setup Routes.
        """
        self.mqtt_topics = []
        try:
            self.mqtt_host = args["mqtt-to-server"]["broker"]["host"]
            self.mqtt_port = args["mqtt-to-server"]["broker"]["port"]
            self.bridge_port = args["server-to-client"]["port"]
            self.stream_protocol = args["server-to-client"]["protocol"]
            logger.info("Using protocol %s" % self.stream_protocol.lower())
            if self.stream_protocol.lower(
            ) != "websocket" and self.stream_protocol.lower() != "sse":
                raise ConfigException("Invalid protocol")
            self.dynamic_subscriptions = dynamic_subscriptions
            if not self.dynamic_subscriptions:
                self.mqtt_topics = args["mqtt-to-server"]["topics"]
        except KeyError as e:
            raise ConfigException("Error when accessing field", e) from e
        logger.info("connecting to mqtt")
        self.topic_dict = {}
        self.ioloop = ioloop
        self.mqtt_client = Client()
        self.mqtt_client.on_message = self.on_mqtt_message
        self.mqtt_client.on_connect = self.on_mqtt_connect
        self.mqtt_client.connect_async(host=self.mqtt_host,
                                       port=self.mqtt_port)
        self.mqtt_client.loop_start()
        self._app = web.Application([
            (r'.*', WebsocketHandler if self.stream_protocol == "websocket"
             else ServeSideEventsHandler, dict(parent=self)),
        ],
                                    debug=True)

    def get_app(self):
        return self._app

    def get_port(self):
        return self.bridge_port

    async def parse_req_path(self, req_path):
        candidate_path = req_path
        if len(candidate_path) is 1 and candidate_path[0] is "/":
            return "#"
        if candidate_path[len(req_path) - 1] is "/":
            candidate_path = candidate_path + "#"
        if candidate_path[0] == "/":
            candidate_path = candidate_path[1:]
        return candidate_path

    def on_mqtt_message(self, client, userdata, message):
        logger.debug("received message on topic %s" % message.topic)

    async def socket_write_message(self, socket, message):
        try:
            await socket.write_message(json.dumps(message))
        except Exception as e:
            logger.error(e)
            try:
                await socket.write_message(message)
            except Exception as e:
                logger.error(e)

    def append_dynamic(self, topic):
        logger.info("adding dynamic subscription for %s " % topic)
        self.message_callback_add_with_sub_topic(topic, dynamic=True)

    def remove_dynamic(self, topic):
        logger.info("removing dynamic subscription for %s " % topic)
        self.topic_dict.pop(topic, None)
        self.mqtt_client.unsubscribe(topic)

    def message_callback_add_with_sub_topic(self, sub_topic, dynamic):
        logger.info("adding callback for mqtt topic: %s" % sub_topic)

        def message_callback(client, userdata, message):
            logger.debug(
                "Recieved Mqtt Message on %s as result of subscription on %s" %
                (message.topic, sub_topic))
            if sub_topic is not message.topic:
                if message.topic not in self.topic_dict[sub_topic]["matches"]:
                    self.topic_dict[sub_topic]["matches"].append(message.topic)
            for topic in self.topic_dict:
                if topic == message.topic:
                    for socket in self.topic_dict[topic]["sockets"]:
                        logger.debug("sending to socket:")
                        logger.debug(socket)
                        self.ioloop.add_callback(
                            self.socket_write_message,
                            socket=socket,
                            message={
                                "topic": message.topic,
                                "payload": message.payload.decode('utf-8')
                            })
                elif message.topic in self.topic_dict[topic]["matches"]:
                    for socket in self.topic_dict[topic]["sockets"]:
                        logger.debug("sending to socket:")
                        logger.debug(socket)
                        self.ioloop.add_callback(
                            self.socket_write_message,
                            socket=socket,
                            message={
                                "topic": message.topic,
                                "payload": message.payload.decode('utf-8')
                            })

        if sub_topic not in self.topic_dict:
            self.mqtt_client.message_callback_add(sub_topic, message_callback)
            self.topic_dict[sub_topic] = {
                "matches": [],
                "sockets": [],
                "dynamic": dynamic
            }
            self.mqtt_client.subscribe(sub_topic)

    def on_mqtt_connect(self, client, userdata, flags, rc):
        logger.info("mqtt connected to broker %s:%s" %
                    (self.mqtt_host, str(self.mqtt_port)))
        for topic in self.mqtt_topics:
            self.message_callback_add_with_sub_topic(topic, dynamic=False)

    async def mqtt_disconnect(self):
        t = Thread(target=self.mqtt_client.disconnect, daemon=True)
        t.start()
        self.mqtt_client.loop_stop()
Example #21
0
 def addTempReference(self, mqttclient: MqttClient, tempRefTopic: str,
                      factor: float):
     if tempRefTopic not in self.tempReferences:
         mqttclient.message_callback_add(tempRefTopic, self.onTempRefValue)
         mqttclient.subscribe(tempRefTopic)
     self.tempReferences[tempRefTopic] = factor
Example #22
0
from paho.mqtt.client import Client

client = Client()
client.username_pw_set('RpiLights', '649t15Db#@4Z')
client.enable_logger()


def topic_callback(client, userdata, message):
    print('topic_callback', message)
    if message.payload.decode('utf-8') == 'on':
        client.publish('topic2', payload='off')


def on_connect(client, userdata, flags, rc):
    print("Connected")
    client.subscribe('topic1')


client.message_callback_add('topic1', topic_callback)
client.on_connect = on_connect

client.connect('cloud.iot-playground.com', port=10048)
print(str(client))

client.subscribe('topic1')
client.publish('topic2', payload=1)

client.loop_forever()
Example #23
0
class LampiApp(App):
    _updated = False
    _updatingUI = False
    _hue = NumericProperty()
    _saturation = NumericProperty()
    _brightness = NumericProperty()
    _preset = NumericProperty()
    lamp_is_on = BooleanProperty()
    _preset_color = NumericProperty()
    _preset_temp = NumericProperty()

    remote_connection = StringProperty("[b]Connected:[/b] No")
    trusted_remotes = StringProperty("[b]Trusted Remotes:[/b] None")

    mp = Mixpanel(MIXPANEL_TOKEN, consumer=AsyncBufferedConsumer())

    def _get_hue(self):
        return self._hue

    def _set_hue(self, value):
        self._hue = value

    def _get_saturation(self):
        return self._saturation

    def _set_saturation(self, value):
        self._saturation = value

    def _get_brightness(self):
        return self._brightness

    def _set_brightness(self, value):
        self._brightness = value

    def _get_preset(self):
        return self._preset

    def _set_preset(self, value):
        self._preset = value

    def _get_preset_color(self):
        return self._preset_color

    def _set_preset_color(self, value):
        self._preset_color = value

    def _get_preset_temp(self):
        return self._preset_temp

    def _set_preset_temp(self, value):
        self._preset_temp = value

    hue = AliasProperty(_get_hue, _set_hue, bind=['_hue'])
    saturation = AliasProperty(_get_saturation,
                               _set_saturation,
                               bind=['_saturation'])
    brightness = AliasProperty(_get_brightness,
                               _set_brightness,
                               bind=['_brightness'])
    preset = AliasProperty(_get_preset, _set_preset, bind=['_preset'])
    preset_color = AliasProperty(_get_preset_color,
                                 _set_preset_color,
                                 bind=['_preset_color'])
    preset_temp = AliasProperty(_get_preset_temp,
                                _set_preset_temp,
                                bind=['_preset_temp'])
    gpio17_pressed = BooleanProperty(False)
    device_associated = BooleanProperty(True)

    def on_start(self):
        self._publish_clock = None
        self.mqtt_broker_bridged = False
        self._associated = True
        self.association_code = None
        self.mqtt = Client(client_id=MQTT_CLIENT_ID)
        self.mqtt.enable_logger()
        self.mqtt.will_set(client_state_topic(MQTT_CLIENT_ID),
                           "0",
                           qos=2,
                           retain=True)
        self.mqtt.on_connect = self.on_connect
        self.mqtt.connect(MQTT_BROKER_HOST,
                          port=MQTT_BROKER_PORT,
                          keepalive=MQTT_BROKER_KEEP_ALIVE_SECS)
        self.mqtt.loop_start()
        self.set_up_GPIO_and_device_status_popup()
        self.associated_status_popup = self._build_associated_status_popup()
        self.associated_status_popup.bind(on_open=self.update_popup_associated)

        self._remote = None
        self._popup_remote = None
        self.pairing_popup = self._build_pairing_popup()

        self._update_remotes_ui()

        self.discoverswitch = self.root.ids.discoverswitch
        self.discoverswitch.bind(active=self.toggle_discovery)

        Clock.schedule_interval(self._poll_associated, 0.1)

    def _build_associated_status_popup(self):
        return Popup(title='Associate your Lamp',
                     content=Label(text='Msg here', font_size='30sp'),
                     size_hint=(1, 1),
                     auto_dismiss=False)

    def _build_pairing_popup(self):
        layout = StackLayout()
        label = Label(
            text=
            'A new remote is attempting\nto connect to your lamp.\n\nWould you like to\nallow it?',
            size_hint=(1, None),
            padding=(4, 4))
        label.bind(size=self._update_textsize)
        deny = Button(text='Deny', size_hint=(0.49, None), height=40)
        allow = Button(text='Trust', size_hint=(0.49, None), height=40)
        allow.on_release = self._allow_remote
        deny.on_release = self._decline_remote
        layout.add_widget(label)
        layout.add_widget(Label(size_hint=(1, None), height=15))
        layout.add_widget(deny)
        layout.add_widget(Label(size_hint=(0.02, None), height=1))
        layout.add_widget(allow)
        return Popup(title='Remote Pairing Request',
                     content=layout,
                     size_hint=(1, 0.68),
                     auto_dismiss=False)

    def _update_textsize(self, instance, value):
        instance.text_size = (value[0], value[1])

    def on_new_remote(self, client, userdata, message):
        isEmpty = message.payload == b''

        if isEmpty:
            self._remote = None
        else:
            remote = json.loads(message.payload.decode('utf-8'))
            self._remote = remote
            self._popup_remote = remote
            if (not remote['allowed']):
                self.pairing_popup.open()

        self._update_remotes_ui()

    def _allow_remote(self):
        print("Pairing allowed for {}".format(self._popup_remote['address']))
        remotes.saveAddress(self._popup_remote['address'])
        self._remote = None
        self._popup_remote = None
        self.pairing_popup.dismiss()
        self._update_remotes_ui()

        # Display confirmation
        conf = Popup(
            title='Remote Trusted',
            content=Label(
                text=
                'You have successfully trusted\nyour remote. Pair it again to\nuse it'
            ),
            size_hint=(1, 0.5),
            auto_dismiss=False)

        conf.open()
        Clock.schedule_once(lambda dt: conf.dismiss(), 3)

    def _decline_remote(self):
        print("Pairing denied for {}".format(self._popup_remote['address']))
        self._popup_remote = None
        self._remote = None
        self.pairing_popup.dismiss()
        self._update_remotes_ui()

    def clear_remotes(self):
        remotes.clear()
        self.mqtt.publish(DISCONNECT_TOPIC, b'')
        self._update_remotes_ui()

    def toggle_discovery(self, instance, value):
        # Send message accordingly
        self.mqtt.publish(DISCOVERY_TOPIC,
                          ("true" if value else "false").encode('utf8'),
                          retain=True)

    def _update_remotes_ui(self):
        savedremotes = remotes._read()
        statustext = "[b]Connected:[/b] False\n\n"

        if (self._remote is not None):
            self.remote_connection = "[b]Connected:[/b] [color=32ff32]{}[/color]".format(
                self._remote['address'])
        else:
            self.remote_connection = "[b]Connected:[/b] [color=ff3232]Not connected[/color]"

        if (len(savedremotes) == 0):
            self.trusted_remotes = "[b]Trusted Remotes:[/b] None"
        else:
            self.trusted_remotes = "[b]Trusted Remotes:[/b]\n" + "\n".join(
                [" • {}".format(addr) for addr in savedremotes])

    def on_hue(self, instance, value):
        if self._updatingUI:
            return
        self._track_ui_event('Slider Change', {
            'slider': 'hue-slider',
            'value': value
        })
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_saturation(self, instance, value):
        if self._updatingUI:
            return
        self._track_ui_event('Slider Change', {
            'slider': 'saturation-slider',
            'value': value
        })
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_brightness(self, instance, value):
        if self._updatingUI:
            return
        self._track_ui_event('Slider Change', {
            'slider': 'brightness-slider',
            'value': value
        })
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_lamp_is_on(self, instance, value):
        if self._updatingUI:
            return
        self._track_ui_event('Toggle Power', {'isOn': value})
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_preset_temp(self, instance, value):
        if self._updatingUI:
            return
        self._track_ui_event('Slider Change', {
            'slider': 'preset_hue_slider',
            'value': value
        })

    def _track_ui_event(self, event_name, additional_props={}):
        device_id = lampi.lampi_util.get_device_id()

        event_props = {
            'event_type': 'ui',
            'interface': 'lampi',
            'device_id': device_id
        }
        event_props.update(additional_props)

        self.mp.track(device_id, event_name, event_props)

    def on_connect(self, client, userdata, flags, rc):
        self.mqtt.publish(client_state_topic(MQTT_CLIENT_ID),
                          b"1",
                          qos=2,
                          retain=True)
        self.mqtt.message_callback_add(TOPIC_LAMP_CHANGE_NOTIFICATION,
                                       self.receive_new_lamp_state)
        self.mqtt.message_callback_add(broker_bridge_connection_topic(),
                                       self.receive_bridge_connection_status)
        self.mqtt.message_callback_add(TOPIC_LAMP_ASSOCIATED,
                                       self.receive_associated)
        self.mqtt.message_callback_add(NEW_REMOTE_TOPIC, self.on_new_remote)
        self.mqtt.subscribe(broker_bridge_connection_topic(), qos=1)
        self.mqtt.subscribe(TOPIC_LAMP_CHANGE_NOTIFICATION, qos=1)
        self.mqtt.subscribe(TOPIC_LAMP_ASSOCIATED, qos=2)
        self.mqtt.subscribe(NEW_REMOTE_TOPIC, qos=2)

    def _poll_associated(self, dt):
        # this polling loop allows us to synchronize changes from the
        #  MQTT callbacks (which happen in a different thread) to the
        #  Kivy UI
        self.device_associated = self._associated

    def receive_associated(self, client, userdata, message):
        # this is called in MQTT event loop thread
        new_associated = json.loads(message.payload.decode('utf-8'))
        if self._associated != new_associated['associated']:
            if not new_associated['associated']:
                self.association_code = new_associated['code']
            else:
                self.association_code = None
            self._associated = new_associated['associated']

    def on_device_associated(self, instance, value):
        if value:
            self.associated_status_popup.dismiss()
        else:
            self.associated_status_popup.open()

    def update_popup_associated(self, instance):
        code = self.association_code[0:6]
        instance.content.text = ("Please use the\n"
                                 "following code\n"
                                 "to associate\n"
                                 "your device\n"
                                 "on the Web\n{}".format(code))

    def receive_bridge_connection_status(self, client, userdata, message):
        # monitor if the MQTT bridge to our cloud broker is up
        if message.payload == b"1":
            self.mqtt_broker_bridged = True
        else:
            self.mqtt_broker_bridged = False

    def receive_new_lamp_state(self, client, userdata, message):
        new_state = json.loads(message.payload.decode('utf-8'))
        Clock.schedule_once(lambda dt: self._update_ui(new_state), 0.01)

    def _update_ui(self, new_state):
        if self._updated and new_state['client'] == MQTT_CLIENT_ID:
            # ignore updates generated by this client, except the first to
            #   make sure the UI is syncrhonized with the lamp_service
            return
        self._updatingUI = True
        try:
            if 'color' in new_state:
                self.hue = new_state['color']['h']
                self.saturation = new_state['color']['s']
            if 'brightness' in new_state:
                self.brightness = new_state['brightness']
            if 'on' in new_state:
                self.lamp_is_on = new_state['on']
        finally:
            self._updatingUI = False

        self._updated = True

    def _update_leds(self):
        msg = {
            'color': {
                'h': self._hue,
                's': self._saturation
            },
            'brightness': self._brightness,
            'on': self.lamp_is_on,
            'client': MQTT_CLIENT_ID
        }
        self.mqtt.publish(TOPIC_SET_LAMP_CONFIG,
                          json.dumps(msg).encode('utf-8'),
                          qos=1)
        self._publish_clock = None

    def set_up_GPIO_and_device_status_popup(self):
        self.pi = pigpio.pi()
        self.pi.set_mode(17, pigpio.INPUT)
        self.pi.set_pull_up_down(17, pigpio.PUD_UP)
        Clock.schedule_interval(self._poll_GPIO, 0.05)
        self.network_status_popup = self._build_network_status_popup()
        self.network_status_popup.bind(on_open=self.update_device_status_popup)

    def _build_network_status_popup(self):
        return Popup(title='Device Status',
                     content=Label(text='IP ADDRESS WILL GO HERE'),
                     size_hint=(1, 1),
                     auto_dismiss=False)

    def update_device_status_popup(self, instance):
        interface = "wlan0"
        ipaddr = lampi.lampi_util.get_ip_address(interface)
        deviceid = lampi.lampi_util.get_device_id()
        msg = ("Version: {}\n"
               "{}: {}\n"
               "DeviceID: {}\n"
               "Broker Bridged: {}\n"
               "Async Analytics").format(LAMPI_APP_VERSION, interface, ipaddr,
                                         deviceid, self.mqtt_broker_bridged)
        instance.content.text = msg

    def on_gpio17_pressed(self, instance, value):
        if value:
            self.network_status_popup.open()
        else:
            self.network_status_popup.dismiss()

    def _poll_GPIO(self, dt):
        # GPIO17 is the rightmost button when looking front of LAMPI
        self.gpio17_pressed = not self.pi.read(17)

    def write_preset(self, num):

        filewrite = {
            "stateList": [
                {
                    "state": {
                        "h": self._preset_color,
                        "s": 1.0,
                        "b": 1.0
                    },
                    "smooth": False,
                    "waitTime": 0,
                    "transitionTime": 0
                },
            ],
            'loop':
            False
        }

        with open(PRESETS[num - 1] + ".json", "w") as f:
            json.dump(filewrite, f)
Example #24
0
def on_Soc(client, userdata, message):
    try:
        val = json.loads(message.payload)
        print("SOC = %s" % val["value"])
    except:
        print("SOC Exception")
        reconnect()


def on_connect(client, userdata, rc, *args):
    client.subscribe(bat_topic)
    client.subscribe(soc_topic)
    #client.subscribe("N/%s/+/+/ProductId" % portal_id)
    #client.subscribe("N/%s/#" % portal_id)


client = Client("P1")
client.tls_set(cert_reqs=ssl.CERT_NONE)
client.tls_insecure_set(True)
client.username_pw_set(username, password=password)
client.connect(mqtt_broker, port=8883)
client.on_connect = on_connect
client.on_message = on_message
client.message_callback_add(bat_topic, on_BatVoltage)
client.message_callback_add(soc_topic, on_Soc)

client.loop_start()

while True:
    sleep(1)
Example #25
0
class MqttBus(IBus):
    """
    Args transport from pusher to subscribers via MQTT
    """
    _subscribers = dict()

    def __init__(self,
                 host=None,
                 *args,
                 publish_time_out=0.05,
                 publish_waiting=0.1,
                 mid_max_len=64,
                 qos=2,
                 retain=True,
                 **kwargs):
        super().__init__()
        if host is None:
            host = 'localhost'
        self.client = Client(*args, **kwargs)
        self.client.connect(host)
        self.client.loop_start()
        self.client.on_publish = self._on_publish
        self._received_messages = []
        self.mid_max_len = mid_max_len
        self.publish_time_out = publish_time_out
        self.publish_waiting = publish_waiting
        self.qos = qos
        self.retain = retain

    @staticmethod
    def _loads(payload):
        try:
            return pickle.loads(payload,
                                fix_imports=True,
                                encoding="utf-8",
                                errors="strict")
        except Exception as e:
            log.error('mqtt loads error:' + str(e))
            return None

    @staticmethod
    def _dumps(data):
        try:
            return pickle.dumps(data, protocol=3, fix_imports=True)
        except Exception as e:
            log.error('mqtt dumps error:' + str(e))
            return None

    def _on_publish(self, client, userdata, mid):
        if len(self._received_messages) >= self.mid_max_len:
            self._received_messages.pop(0)
            self._received_messages.append(mid)
        else:
            self._received_messages.append(mid)

    def _on_message(self, client, userdata, msg):

        args, kwargs = self._loads(msg.payload)
        if self._subscribers.get(msg.topic):
            for fn in self._subscribers[msg.topic]:
                fn(*args, **kwargs)
        else:
            log.warning(
                'mqtt Callback _subscribers is not set for topic={}'.format(
                    msg.topic))

    def subscribe(self, topic, fn=None):
        """
        Add function fn to subscribers list
        """
        if fn:
            assert hasattr(fn, '__call__')
            self.client.subscribe(topic, qos=self.qos)
            self.client.message_callback_add(
                topic, lambda client, userdata, msg: self._on_message(
                    client, userdata, msg))
            if self._subscribers.get(topic) is None:
                self._subscribers[topic] = [fn]
                log.info('mqtt Function {} subscribed to topic "{}"'.format(
                    fn.__name__, topic))
            else:
                self._subscribers[topic].append(fn)
                log.info('mqtt Function {} subscribed to topic "{}"'.format(
                    fn.__name__, topic))
        else:
            self.client.message_callback_remove(topic)
            self.client.unsubscribe(topic)

    def push(self, topic, *args, **kwargs):
        """
        Call all the subscribers
        """
        _args = [arg for arg in args if isinstance(arg, (float, int, str))]
        _kwargs = {
            key: arg
            for key, arg in kwargs.items()
            if isinstance(arg, (float, int, str))
        }

        if _args or _kwargs:
            log.debug(
                'pushed topic "{}" with args {} and kwargs {} and {}'.format(
                    topic, str(_args), str(_kwargs),
                    str(set(kwargs.keys()) - set(_kwargs.keys()))))

        send_data = self._dumps((args, kwargs))
        log.info('mqtt Pushing data with size={} to topic={}...'.format(
            len(send_data), topic))
        rc, mid = self.client.publish(topic,
                                      send_data,
                                      qos=self.qos,
                                      retain=self.retain)
        publish_time = time.time()
        while mid not in self._received_messages:
            time.sleep(self.publish_time_out)
            if (time.time() - publish_time) >= self.publish_waiting:
                break
        if rc == 0:
            log.info('mqtt Pushed data to topic={} with rc {}'.format(
                topic, rc))
        else:
            log.warning('mqtt Not pushed data to topic={} with rc {}'.format(
                topic, rc))
class SphinxApp:
    _updated = False
    _hue = 0.0
    _saturation = 0.0
    _brightness = 0.0
    lamp_is_on = 0

    #def _get_hue(self):        return self._hue
    #def _set_hue(self, value): self._hue = value

    #def _get_saturation(self):        return self._saturation
    #def _set_saturation(self, value): self._saturation = value

    #def _get_brightness(self):        return self._brightness
    #def _set_brightness(self, value): self._brightness = value

    hue = 0  # AliasProperty(_get_hue, _set_hue, bind=['_hue'])
    saturation = 0  # AliasProperty(_get_saturation, _set_saturation, bind=['_saturation'])
    brightness = 0  # AliasProperty(_get_brightness, _set_brightness, bind=['_brightness'])
    gpio17_pressed = 0  # BooleanProperty(False)
    device_associated = 0  # BooleanProperty(True)

    def on_start(self):
        self._publish_clock = None
        self.mqtt_broker_bridged = False
        self._associated = True
        self.association_code = None
        self.mqtt = Client(client_id=MQTT_CLIENT_ID)
        self.mqtt.enable_logger()
        self.mqtt.will_set(client_state_topic(MQTT_CLIENT_ID),
                           "0",
                           qos=2,
                           retain=True)
        self.mqtt.on_connect = self.on_connect
        self.mqtt.connect(MQTT_BROKER_HOST,
                          port=MQTT_BROKER_PORT,
                          keepalive=MQTT_BROKER_KEEP_ALIVE_SECS)
        print("broker host", MQTT_BROKER_HOST, " mqtt broker port",
              MQTT_BROKER_PORT)
        #self.mqtt.loop_forever() #
        self.mqtt.loop_start()
        #self.set_up_GPIO_and_network_status_popup()
        #self.associated_status_popup = self._build_associated_status_popup()
        #self.associated_status_popup.bind(on_open=self.update_popup_associated)
        Clock.schedule_interval(self._poll_associated, 0.1)
        print("hue", self._hue, self.hue)
        print("saturation", self._saturation, self.saturation)
        print("brightness", self._brightness, self.brightness)
        print("onoff", self.lamp_is_on, Clock)

    def _build_associated_status_popup(self):
        return Popup(title='Associate your Lamp',
                     content=Label(text='Msg here', font_size='30sp'),
                     size_hint=(1, 1),
                     auto_dismiss=False)

    def on_hue(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_saturation(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_brightness(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_lamp_is_on(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_connect(self, client, userdata, flags, rc):
        print("lampi_sphinx_app on_connect")
        self.mqtt.publish(client_state_topic(MQTT_CLIENT_ID),
                          b"1",
                          qos=2,
                          retain=True)

        self.mqtt.message_callback_add(TOPIC_LAMP_CHANGE_NOTIFICATION,
                                       self.receive_new_lamp_state)
        self.mqtt.message_callback_add(broker_bridge_connection_topic(),
                                       self.receive_bridge_connection_status)
        self.mqtt.message_callback_add(TOPIC_LAMP_ASSOCIATED,
                                       self.receive_associated)

        self.mqtt.subscribe(broker_bridge_connection_topic(), qos=1)
        self.mqtt.subscribe(TOPIC_LAMP_CHANGE_NOTIFICATION, qos=1)
        self.mqtt.subscribe(TOPIC_LAMP_ASSOCIATED, qos=2)

    def _poll_associated(self, dt):
        # this polling loop allows us to synchronize changes from the
        #  MQTT callbacks (which happen in a different thread) to the
        #  Kivy UI
        print("lampi_sphinx_app _poll_associated")
        self.device_associated = self._associated

    def receive_associated(self, client, userdata, message):
        print("lampi_sphinx_app receive_assocaited")
        # this is called in MQTT event loop thread
        new_associated = json.loads(message.payload.decode('utf-8'))
        if self._associated != new_associated['associated']:
            if not new_associated['associated']:
                self.association_code = new_associated['code']
            else:
                self.association_code = None
            self._associated = new_associated['associated']

    def on_device_associated(self, instance, value):
        if value:
            self.associated_status_popup.dismiss()
        else:
            self.associated_status_popup.open()

    def update_popup_associated(self, instance):
        code = self.association_code[0:6]
        instance.content.text = ("Please use the\n"
                                 "following code\n"
                                 "to associate\n"
                                 "your device\n"
                                 "on the Web\n{}".format(code))

    def receive_bridge_connection_status(self, client, userdata, message):
        print("lampi_sphinx_app receive_bridge_connection_status", userdata,
              message)
        # monitor if the MQTT bridge to our cloud broker is up
        if message.payload == b"1":
            self.mqtt_broker_bridged = True
        else:
            self.mqtt_broker_bridged = False

    def update_new_config(self, config):
        msg = {
            'color': {
                'h': self.hue,
                's': self.saturation
            },
            'brightness': self.brightness,
            'on': self.lamp_is_on,
            'client': MQTT_CLIENT_ID
        }

        print("DEBUG update_new_config", config)
        print("current config:", msg)
        for piece in config:
            if type(piece) == type({}):
                print("piece", piece)
                for key in piece.keys():
                    if 'h' == key:
                        print("Setting hue to:", piece[key])
                        self.hue = piece[key]
                    if 's' == key:
                        print("Setting sat to:", piece[key])
                        self.saturation = piece[key]
                    if 'b' == key:
                        print("Setting brightness to:", piece[key])
                        self.brightness = piece[key]
            elif type(piece) == type(""):
                if piece.find("LAMPI SET POWER") != -1:
                    if piece.find("0") != -1:
                        self.lamp_is_on = False
                    elif piece.find("1") != -1:
                        self.lamp_is_on = True
                    else:
                        print("Can't detect piece1!", piece)
                elif piece.find("TOGGLE") != -1:
                    self.lamp_is_on = not self.lamp_is_on
            else:
                print("Can't detect piece2!", piece)

        msg = {
            'color': {
                'h': self.hue,
                's': self.saturation
            },
            'brightness': self.brightness,
            'on': self.lamp_is_on,
            'client': MQTT_CLIENT_ID
        }

        self._update_leds()
        print("config now:", msg)
        #    if 'color' in new_state:
        #        self.hue = new_state['color']['h']
        #        self.saturation = new_state['color']['s']
        #    if 'brightness' in new_state:
        #        self.brightness = new_state['brightness']
        #    if 'on' in new_state:
        #        self.lamp_is_on = new_state['on']

    def receive_new_lamp_state(self, client, userdata, message):
        print("lampi_sphinx_app receive_new_lamp_state", userdata, message)
        new_state = json.loads(message.payload.decode('utf-8'))
        self._update_ui(new_state)
        #Clock.schedule_once(lambda dt: self._update_ui(new_state), 0.01)
        print("updateUI Scheduled!")

    def _update_ui(self, new_state):
        print("lamp_sphinx_app _update_ui", new_state)
        if self._updated and new_state['client'] == MQTT_CLIENT_ID:
            # ignore updates generated by this client, except the first to
            #   make sure the UI is syncrhonized with the lamp_service
            return
        try:
            if 'color' in new_state:
                self.hue = new_state['color']['h']
                self.saturation = new_state['color']['s']
            if 'brightness' in new_state:
                self.brightness = new_state['brightness']
            if 'on' in new_state:
                self.lamp_is_on = new_state['on']
        finally:
            pass

        self._updated = True

    def _update_leds(self):
        msg = {
            'color': {
                'h': self.hue,
                's': self.saturation
            },
            'brightness': self.brightness,
            'on': self.lamp_is_on,
            'client': MQTT_CLIENT_ID
        }
        self.mqtt.publish(TOPIC_SET_LAMP_CONFIG,
                          json.dumps(msg).encode('utf-8'),
                          qos=1)
        self._publish_clock = None
Example #27
0
#

if __name__ == "__main__":

    broker = "wild.mat.ucm.es"
    choques = "clients/stop"  #topic=choques+"/servidor...
    ###choques: para evitar colisiones en el broker en las pruebas

    nombre_usuario = input("¿nombre usuario? ")  #identificador del usuario

    mqttc = Client(userdata=[nombre_usuario, 0, 0])
    #userdata=[nombre_usuario,puntos_usuario,numero_partida_usuario]

    #funciones callback:
    #redirigimos los mensajes según el topic del que vengan para mayor claridad
    mqttc.message_callback_add(choques + "/servidor/#", callback_servidor)
    mqttc.message_callback_add(choques + "/partidas/#", callback_partidas)
    mqttc.message_callback_add(choques + "/jugadores/" + nombre_usuario,
                               callback_jugadores)

    #will_set:
    #ultimo mensaje que se envía si el Client se desconecta sin usar disconnect()
    mqttc.will_set(choques + "/jugadores/" + nombre_usuario,
                   payload="DISCONNECT")

    mqttc.connect(broker)

    #suscripciones iniciales del cliente
    mqttc.subscribe(choques + "/jugadores/" + nombre_usuario)
    mqttc.subscribe(choques + "/servidor")
    mqttc.subscribe(choques + "/servidor/" + nombre_usuario)
Example #28
0
        iii = input("¿Puntuación máxima?\n->")
        max_puntuacion = max(50, int(iii))
    except:
        max_puntuacion = 500
    if min_jugadores_partida > max_jugadores_partida:
        min_jugadores_partida = 2

    mqttc = Client(
        userdata={})  #diccionario como userdata para la info del juego
    #'info' indica el estado de la partida:
    #estado: 0 es sin empezar,1 en espera,2 jugando,3 en recuento
    #alfabeto: las letras que quedan por jugar, de inicio ya están desordenadas
    #confirmados para tener una forma de ver si todos envian la info

    #funciones callback:
    mqttc.message_callback_add(choques + "/jugadores/#", callback_jugadores)
    mqttc.message_callback_add(choques + "/partidas/#", callback_partidas)
    mqttc.message_callback_add(choques + "/servidor/#", callback_servidor)
    mqttc.message_callback_add(choques + "/solicitudes/#",
                               callback_solicitudes)

    #will_set:
    #ultimo mensaje que se envía si el Client se desconecta sin usar disconnect()
    mqttc.will_set(choques + "/servidor", payload="SERVER_FAIL")

    mqttc.connect(broker)

    mqttc.publish(choques + "/servidor", payload="SERVER_READY")
    print("\nSERVIDOR ACTIVO...")
    print("\nDATOS del juego")
    print("-nº mínimo de jugadores por partida:", min_jugadores_partida)
Example #29
0
class MqttClient:
    def __init__(self) -> None:
        self.client = Client()
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.on_publish = self.on_publish
        self.client.on_disconnect = self.on_disconnect
        self.client.on_unsubscribe = self.on_unsubscribe
        self.client.on_subscribe = self.on_subscribe

    def connect(self,
                host: str,
                port: int,
                user: str,
                passwd: str,
                keepalive: int = 600):
        """connect server"""

        # set user passwd
        self.client.username_pw_set(user, passwd)
        # connect
        self.client.connect(host=host, port=port, keepalive=keepalive)

    def loop_start(self):
        """loop start"""

        self.client.loop_start()

    def loop_forever(self):
        """loop forever"""

        self.client.loop_forever()

    def subscribe(self, topic: str):
        """subscribe topic"""

        self.client.subscribe(topic)

    def publish(self, topic, msg, qos=0, retain=False, properties=None):
        """publish msg"""

        payload = json.dumps(msg, ensure_ascii=False)
        self.client.publish(topic=topic,
                            payload=payload,
                            qos=qos,
                            retain=retain,
                            properties=properties)

    def add_callback(self, topic, callback):
        """message_callback_add"""

        self.client.message_callback_add(topic, callback)

    def on_connect(self, client, userdata, flags, rc):
        """连接事件"""

        logger.info('on_connect'.center(40, '*'))
        logger.info(f'Connected with result code: {rc}')

    def on_disconnect(self, client, userdata, rc):
        """断开连接事件"""

        # logger.info('on_disconnect'.center(40, '*'))
        # logger.info('Unexpected disconnected rc = {rc}')
        pass

    def on_subscribe(self, client, userdata, mid, granted_qos):
        """订阅事件"""

        logger.info('on_subscribe'.center(40, '*'))
        logger.info('on_subscribe: qos = {granted_qos}')

    def on_unsubscribe(self, client, userdata, mid):
        """取消订阅事件"""

        # logger.info('on_unsubscribe'.center(40, '*'))
        # logger.info('on_unsubscribe: qos = {granted_qos}')
        pass

    def on_publish(self, client, userdata, mid):
        """发布消息事件"""

        logger.info('on_publish'.center(40, '*'))
        logger.info(f'on_publish: mid = {mid}')

    def on_message(self, client, userdata, msg):
        """获得消息事件,触发动作,匹配不到 message_callback_add 时使用这个"""

        logger.info('on_message'.center(40, '*'))
        payload = msg.payload.decode('utf-8')
        logger.info(f'on_message topic: {msg.topic}')
        logger.info(payload)
Example #30
0
class LampiApp(App):
    _updatingUI = False
    _hue = NumericProperty()
    _saturation = NumericProperty()
    _brightness = NumericProperty()
    lamp_is_on = BooleanProperty()
    just_turned_on = True

    def _get_hue(self):
        return self._hue

    def _set_hue(self, value):
        self._hue = value

    def _get_saturation(self):
        return self._saturation

    def _set_saturation(self, value):
        self._saturation = value

    def _get_brightness(self):
        return self._brightness

    def _set_brightness(self, value):
        self._brightness = value

    hue = AliasProperty(_get_hue, _set_hue, bind=['_hue'])
    saturation = AliasProperty(_get_saturation, _set_saturation,
                               bind=['_saturation'])
    brightness = AliasProperty(_get_brightness, _set_brightness,
                               bind=['_brightness'])
    gpio17_pressed = BooleanProperty(False)

    def on_start(self):
        self._publish_clock = None
        self.mqtt = Client(client_id=MQTT_CLIENT_ID, protocol=MQTT_VERSION)
        self.mqtt.on_connect = self.on_connect
        self.mqtt.connect(MQTT_BROKER_HOST, port=MQTT_BROKER_PORT,
                          keepalive=MQTT_BROKER_KEEP_ALIVE_SECS)
        self.mqtt.will_set('lamp/connection/lamp_ui/state', "0", qos=2, retain=True)
        self.mqtt.publish('lamp/connection/lamp_ui/state', "1", qos=2, retain=True)
        self.mqtt.loop_start()
        self.set_up_GPIO_and_IP_popup()

    def on_hue(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_saturation(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_brightness(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_lamp_is_on(self, instance, value):
        if self._updatingUI:
            return
        if self._publish_clock is None:
            self._publish_clock = Clock.schedule_once(
                lambda dt: self._update_leds(), 0.01)

    def on_connect(self, client, userdata, flags, rc):
        self.mqtt.subscribe(TOPIC_LAMP_CHANGE_NOTIFICATION)
        self.mqtt.message_callback_add(TOPIC_LAMP_CHANGE_NOTIFICATION,
                                       self.receive_new_lamp_state)

    def receive_new_lamp_state(self, client, userdata, message):
        new_state = json.loads(message.payload)
        Clock.schedule_once(lambda dt: self._update_ui(new_state), 0.01)

    def _update_ui(self, new_state):
        self._updatingUI = True
        try:
            if 'client' in new_state:
                if new_state['client'] == 'lamp_ui' and not self.just_turned_on:
                    return
            if 'color' in new_state:
                self.hue = new_state['color']['h']
                self.saturation = new_state['color']['s']
            if 'brightness' in new_state:
                self.brightness = new_state['brightness']
            if 'on' in new_state:
                self.lamp_is_on = new_state['on']
        finally:
            self._updatingUI = False

    def _update_leds(self):
        msg = {'color': {'h': self._hue, 's': self._saturation},
               'brightness': self._brightness,
               'on': self.lamp_is_on,
               'client': 'lamp_ui'}
        self.mqtt.publish(TOPIC_SET_LAMP_CONFIG, json.dumps(msg), qos = 1)
        self._publish_clock = None

    def set_up_GPIO_and_IP_popup(self):
        self.pi = pigpio.pi()
        self.pi.set_mode(17, pigpio.INPUT)
        self.pi.set_pull_up_down(17, pigpio.PUD_UP)
        Clock.schedule_interval(self._poll_GPIO, 0.05)
        self.popup = Popup(title='IP Addresses',
                           content=Label(text='IP ADDRESS WILL GO HERE'),
                           size_hint=(1, 1), auto_dismiss=False)
        self.popup.bind(on_open=self.update_popup_ip_address)

    def update_popup_ip_address(self, instance):
        interface = "wlan0"
        ipaddr = lampi_util.get_ip_address(interface)
        instance.content.text = "{}: {}".format(interface, ipaddr)

    def on_gpio17_pressed(self, instance, value):
        if value:
            self.popup.open()
        else:
            self.popup.dismiss()

    def _poll_GPIO(self, dt):
        # GPIO17 is the rightmost button when looking front of LAMPI
        self.gpio17_pressed = not self.pi.read(17)