Ver Fonte

add mqtt neuron

nico há 7 anos atrás
pai
commit
a91192f4dd

+ 145 - 0
kalliope/neurons/mqtt_publisher/README.md

@@ -0,0 +1,145 @@
+# MQTT Publisher
+
+## Synopsis
+
+Publish a message to a MQTT broker server
+
+## Installation
+
+This is a core neuron, no installation required.
+
+## Options
+
+| parameter    | required | type    | default  | choices             | comment                                                                                          |
+|--------------|----------|---------|----------|---------------------|--------------------------------------------------------------------------------------------------|
+| broker_ip    | YES      | string  |          |                     | IP address of the MQTT broker server                                                             |
+| port         | NO       | int     | 1883     |                     | Port of the broker. By default 1883. 8883 when TLS is activated.                                 |
+| topic        | YES      | string  |          |                     | Topic name where the message will be published                                                   |
+| payload      | YES      | string  |          |                     | Message to publish on the topic                                                                  |
+| qos          | NO       | int     | 0        | 0 or 1 or 2         | The quality of service level to use                                                              |
+| retain       | NO       | Boolean | FALSE    | True, False         | if set to True, the message will be set as the “last known good”/retained message for the topic. |
+| client_id    | NO       | string  | kalliope |                     | The MQTT client id to use. If not set, the name will be set to "kalliope"                        |
+| keepalive    | NO       | int     | 60       |                     | The keepalive timeout value for the client                                                       |
+| username     | NO       | string  |          |                     | username for authenticating the client                                                           |
+| password     | NO       | string  |          |                     | password for authenticating the client                                                           |
+| ca_cert      | NO       | string  |          |                     | Path to the remote server CA certificate used for securing the transport                         |
+| certfile     | NO       | string  |          |                     | Path to the client certificate file used for authentication                                      |
+| keyfile      | NO       | string  |          |                     | Path to the client key file attached to the client certificate                                   |
+| protocol     | NO       | string  | MQTTv311 | MQTTv31 or MQTTv311 | Can be either MQTTv31 or MQTTv311                                                                |
+| tls_insecure | NO       | string  | FALSE    |                     | Set the verification of the server hostname in the server certificate                            |
+
+## Return Values
+
+No returned values
+
+## Synapses example
+
+Publish a message to the topic "my/topic" with minimal configuration
+```yml
+- name: "mqtt-publisher-1"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "my/topic"
+        payload: "my message"
+```
+
+Publish a json formatted message. Note that anti-slashes must be escaped.
+```yml
+- name: "mqtt-publisher-2"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "mytopic"
+        payload: "{\"mykey\": \"myvalue\"}"
+```
+
+The broker require authentication
+```yml
+- name: "mqtt-publisher-3"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "my/topic"
+        payload: "my message"
+        username: "guest"
+        password: "guest"
+```
+
+The broker require a secure TLS connection
+```yml
+- name: "mqtt-publisher-4"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "my/topic"
+        payload: "my message"
+        ca_cert: "/path/to/ca.cert"
+```
+
+The broker require a secure TLS connection and authentication based on client certificate
+```yml
+- name: "mqtt-publisher-5"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "my/topic"
+        payload: "my message"
+        ca_cert: "/path/to/ca.cert"
+        certfile: "path/to/client.crt"
+        keyfile: "path/to/client.key"
+```
+
+The broker require a secure TLS connection, an authentication based on client certificate and the CA is a self signed certificate
+```yml
+- name: "mqtt-publisher-6"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "my/topic"
+        payload: "my message"
+        ca_cert: "/path/to/ca.cert"
+        certfile: "path/to/client.crt"
+        keyfile: "path/to/client.key"
+        tls_insecure: True
+```
+
+
+## Test with CLI
+
+The following part of the documentation can help you to configure your synapse with right options.
+From here we suppose that you have already a running broker server on your local machine. If it's not the case, please refer to the documentation of the [signal mqtt_subscriber](../../signals/mqtt_subscriber) to install a testing broker server.
+
+Install a CLI mqtt client
+```bash
+sudo apt-get install mosquitto-clients 
+```
+
+Run a subscriber
+```bash
+mosquitto_sub -t 'this/is/a/topic'
+```
+
+Then use your neuron. E.g
+```yml
+- name: "test-mqtt-publisher"
+  signals:
+    - order: "this is my order"
+  neurons:
+    - mqtt_publisher:
+        broker_ip: "127.0.0.1"
+        topic: "this/is/a/topic"
+        payload: "info"
+```

+ 0 - 0
kalliope/neurons/mqtt_publisher/__init__.py


+ 143 - 0
kalliope/neurons/mqtt_publisher/mqtt_publisher.py

@@ -0,0 +1,143 @@
+import logging
+import socket
+
+import paho
+import paho.mqtt.client as mqtt
+
+from kalliope.core.NeuronModule import NeuronModule
+
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
+
+class Mqtt_publisher(NeuronModule):
+    def __init__(self, **kwargs):
+        super(Mqtt_publisher, self).__init__(**kwargs)
+
+        logger.debug("[mqtt_publisher] neuron called with parameters: %s" % kwargs)
+
+        # get parameters
+        self.broker_ip = kwargs.get('broker_ip', None)
+        self.port = kwargs.get('port', 1883)
+        self.topic = kwargs.get('topic', None)
+        self.payload = kwargs.get('payload', None)
+        self.qos = kwargs.get('qos', 0)
+        self.retain = kwargs.get('retain', False)
+        self.client_id = kwargs.get('client_id', 'kalliope')
+        self.keepalive = kwargs.get('keepalive', 60)
+        self.username = kwargs.get('username', None)
+        self.password = kwargs.get('password', None)
+        self.ca_cert = kwargs.get('ca_cert', None)
+        self.certfile = kwargs.get('certfile', None)
+        self.keyfile = kwargs.get('keyfile', None)
+        self.protocol = kwargs.get('protocol', 'MQTTv311')
+        self.tls_insecure = kwargs.get('tls_insecure', False)
+
+        if not self._is_parameters_ok():
+            logger.debug("[mqtt_publisher] One or more invalid parameters, neuron will not be launched")
+        else:
+            # string must be converted
+            self.protocol = self._get_protocol(self.protocol)
+
+            self.client = mqtt.Client(client_id=self.broker_ip, protocol=self.protocol)
+
+            if self.username is not None and self.password is not None:
+                logger.debug("[mqtt_publisher] Username and password are set")
+                self.client.username_pw_set(self.username, self.password)
+
+            if self.ca_cert is not None and self.certfile is not None and self.keyfile is not None:
+                logger.debug("[mqtt_publisher] Active TLS with client certificate authentication")
+                self.client.tls_set(ca_certs=self.ca_cert,
+                                    certfile=self.certfile,
+                                    keyfile=self.keyfile)
+                self.client.tls_insecure_set(self.tls_insecure)
+
+            elif self.ca_cert is not None:
+                logger.debug("[mqtt_publisher] Active TLS with server CA certificate only")
+                self.client.tls_set(ca_certs=self.ca_cert)
+                self.client.tls_insecure_set(self.tls_insecure)
+
+            try:
+                self.client.connect(self.broker_ip, port=self.port, keepalive=self.keepalive)
+                self.client.publish(topic=self.topic, payload=self.payload, qos=int(self.qos), retain=self.retain)
+                logger.debug("[mqtt_publisher] Message published to topic %s: %s" % (self.topic, self.payload))
+                self.client.disconnect()
+            except socket.error:
+                logger.debug("[mqtt_publisher] Unable to connect to broker %s" % self.broker_ip)
+
+    def _is_parameters_ok(self):
+        if self.broker_ip is None:
+            print("[mqtt_publisher] ERROR: broker_ip is not set")
+            return False
+
+        if self.port is not None:
+            if not isinstance(self.port, int):
+                try:
+                    self.port = int(self.port)
+                except ValueError:
+                    print("[mqtt_publisher] ERROR: port must be an integer")
+                    return False
+
+        if self.topic is None:
+            print("[mqtt_publisher] ERROR: topic is not set")
+            return False
+
+        if self.payload is None:
+            print("[mqtt_publisher] ERROR: payload is not set")
+            return False
+
+        if self.qos:
+            if not isinstance(self.qos, int):
+                try:
+                    self.qos = int(self.qos)
+                except ValueError:
+                    print("[mqtt_publisher] ERROR: qos must be an integer")
+                    return False
+            if self.qos not in [0, 1, 2]:
+                print("[mqtt_publisher] ERROR: qos must be 0,1 or 2")
+                return False
+
+        if self.keepalive:
+            if not isinstance(self.keepalive, int):
+                try:
+                    self.keepalive = int(self.keepalive)
+                except ValueError:
+                    print("[mqtt_publisher] ERROR: keepalive must be an integer")
+                    return False
+
+        if self.username is not None and self.password is None:
+            print("[mqtt_publisher] ERROR: password must be set when using username")
+            return False
+        if self.username is None and self.password is not None:
+            print("[mqtt_publisher] ERROR: username must be set when using password")
+            return False
+
+        if self.protocol:
+            if self.protocol not in ["MQTTv31", "MQTTv311"]:
+                print("[mqtt_publisher] Invalid protocol value, fallback to MQTTv311")
+                self.protocol = "MQTTv311"
+
+        # if the user set a certfile, the key and ca cert must be set to
+        if self.certfile is not None and self.keyfile is None:
+            print("[mqtt_publisher] ERROR: keyfile must be set when using certfile")
+            return False
+        if self.certfile is None and self.keyfile is not None:
+            print("[mqtt_publisher] ERROR: certfile must be set when using keyfile")
+            return False
+
+        if self.certfile is not None and self.keyfile is not None:
+            if self.ca_cert is None:
+                print("[mqtt_publisher] ERROR: ca_cert must be set when using keyfile and certfile")
+                return False
+
+        return True
+
+    def _get_protocol(self, protocol):
+        """
+        Return the right code depending on the given string protocol name
+        :param protocol: string name of the protocol to use.
+        :return: integer
+        """
+        if protocol == "MQTTv31":
+            return paho.mqtt.client.MQTTv31
+        return paho.mqtt.client.MQTTv311