From 2bb4a53bd3aeb0a89f852d0efedbb26ff4eaf790 Mon Sep 17 00:00:00 2001 From: sfam Date: Wed, 19 Aug 2015 01:24:40 +0100 Subject: [PATCH 1/8] Add MQTT Switch --- homeassistant/components/switch/mqtt.py | 146 ++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 homeassistant/components/switch/mqtt.py diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py new file mode 100644 index 00000000000..ee38c5fbb43 --- /dev/null +++ b/homeassistant/components/switch/mqtt.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +""" +homeassistant.components.switch.mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a MQTT switch. + +In an ideal scenario, the MQTT device will have a state topic to publish state changes. +If these messages are published with RETAIN flag, the MQTT switch will receive +an instant state update after subscription and will start with correct state. Otherwise, +the initial state of the switch will be false/off. + +When a state topic is not available, the switch will work in an optimistic mode. +In this mode, the MQTT switch will immediately change state after every command. +Otherwise, the switch will wait for state confirmation from device (message from state_topic). + +Optimistic mode can be forced, even if state topic is available. +Try to enable it, if experiencing incorrect switch operation. + + +Configuration: + +switch: + platform: mqtt + name: "Bedroom Switch" + state_topic: "home/bedroom/switch1" + command_topic: "home/bedroom/switch1/set" + payload_on: "ON" + payload_off: "OFF" + optimistic: false + +Variables: + +name +*Optional +The name of the switch. Default is 'MQTT Switch'. + +state_topic +*Optional +The MQTT topic subscribed to receive state updates. +If not specified, optimistic mode will be forced. + +command_topic +*Required +The MQTT topic to publish commands to change the switch state. + +payload_on +*Optional +The payload that represents enabled state. Default is "ON". + +payload_off +*Optional +The payload that represents disabled state. Default is "OFF". + +optimistic +*Optional +Flag that defines if switch works in optimistic mode. Default is false. + +""" + +import logging +import homeassistant.components.mqtt as mqtt +from homeassistant.components.switch import SwitchDevice + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "MQTT Switch" +DEFAULT_PAYLOAD_ON = "ON" +DEFAULT_PAYLOAD_OFF = "OFF" +DEFAULT_OPTIMISTIC = False + +DEPENDENCIES = ['mqtt'] + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Add MQTT Switch """ + + if config.get('command_topic') is None: + _LOGGER.error("Missing required variable: command_topic") + return False + + add_devices_callback([MqttSwitch( + hass, + config.get('name', DEFAULT_NAME), + config.get('state_topic'), + config.get('command_topic'), + config.get('payload_on', DEFAULT_PAYLOAD_ON), + config.get('payload_off', DEFAULT_PAYLOAD_OFF), + config.get('optimistic', DEFAULT_OPTIMISTIC))]) + +class MqttSwitch(SwitchDevice): + """ Represents a switch that can be togggled using MQTT """ + def __init__(self, hass, name, state_topic, command_topic, payload_on, payload_off, optimistic): + self._state = False + self._hass = hass + self._name = name + self._state_topic = state_topic + self._command_topic = command_topic + self._payload_on = payload_on + self._payload_off = payload_off + self._optimistic = optimistic + + def message_received(topic, payload, qos): + if (payload == self._payload_on): + self._state = True + self.update_ha_state() + elif (payload == self._payload_off): + self._state = False + self.update_ha_state() + + if self._state_topic is None: + """ force optimistic mode """ + self._optimistic = True + else: + """ subscribe the state_topic """ + mqtt.subscribe(hass, self._state_topic, message_received) + + @property + def should_poll(self): + """ No polling needed """ + return False + + @property + def name(self): + """ The name of the switch """ + return self._name + + @property + def is_on(self): + """ True if device is on. """ + return self._state + + def turn_on(self, **kwargs): + """ Turn the device on. """ + mqtt.publish(self.hass, self._command_topic, self._payload_on) + if self._optimistic: + """ optimistically assume that switch has changed state """ + self._state = True + self.update_ha_state() + + def turn_off(self, **kwargs): + """ Turn the device off. """ + mqtt.publish(self.hass, self._command_topic, self._payload_off) + if self._optimistic: + """ optimistically assume that switch has changed state """ + self._state = False + self.update_ha_state() From c5db42677a2682c76df026cbf5168834b641a7b2 Mon Sep 17 00:00:00 2001 From: sfam Date: Wed, 19 Aug 2015 01:25:05 +0100 Subject: [PATCH 2/8] Add MQTT Sensor --- homeassistant/components/sensor/mqtt.py | 89 +++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 homeassistant/components/sensor/mqtt.py diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py new file mode 100644 index 00000000000..80e21d16e19 --- /dev/null +++ b/homeassistant/components/sensor/mqtt.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +homeassistant.components.sensor.mqtt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Allows to configure a MQTT sensor. + +This generic sensor implementation uses the MQTT message payload as the sensor value. + +sensor: + platform: mqtt + name: "MQTT Sensor" + state_topic: "home/bedroom/temperature" + unit_of_measurement: "ÂșC" + +Variables: + +name +*Optional +The name of the sensor. Default is 'MQTT Sensor'. + +state_topic +*Required +The MQTT topic subscribed to receive sensor values. + +unit_of_measurement +*Optional +Defines the units of measurement of the sensor, if any. + +""" + +import logging +from homeassistant.helpers.entity import Entity +import homeassistant.components.mqtt as mqtt + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = "MQTT Sensor" + +DEPENDENCIES = ['mqtt'] + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices_callback, discovery_info=None): + """ Add MQTT Sensor """ + + if config.get('state_topic') is None: + _LOGGER.error("Missing required variable: state_topic") + return False + + add_devices_callback([MqttSensor( + hass, + config.get('name',DEFAULT_NAME), + config.get('state_topic'), + config.get('unit_of_measurement'))]) + + +class MqttSensor(Entity): + """ Represents a sensor that can be updated using MQTT """ + def __init__(self, hass, name, state_topic, unit_of_measurement): + self._state = "-" + self._hass = hass + self._name = name + self._state_topic = state_topic + self._unit_of_measurement = unit_of_measurement + + def message_received(topic, payload, qos): + self._state = payload + self.update_ha_state() + + mqtt.subscribe(hass, self._state_topic, message_received) + + @property + def should_poll(self): + """ No polling needed """ + return False + + @property + def name(self): + """ The name of the sensor """ + return self._name + + @property + def unit_of_measurement(self): + """ Unit this state is expressed in. """ + return self._unit_of_measurement + + @property + def state(self): + """ Returns the state of the entity. """ + return self._state From 44263752ca1cd8c1f021312adbc8e09da97f40d2 Mon Sep 17 00:00:00 2001 From: sfam Date: Thu, 20 Aug 2015 23:05:51 +0100 Subject: [PATCH 3/8] update header --- homeassistant/components/sensor/mqtt.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 80e21d16e19..1af8614a7dc 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -4,7 +4,10 @@ homeassistant.components.sensor.mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a MQTT sensor. -This generic sensor implementation uses the MQTT message payload as the sensor value. +This generic sensor implementation uses the MQTT message payload +as the sensor value. If messages in this state_topic are published +with RETAIN flag, the sensor will receive an instant update with +last known value. Otherwise, the initial state will be undefined. sensor: platform: mqtt @@ -20,7 +23,7 @@ The name of the sensor. Default is 'MQTT Sensor'. state_topic *Required -The MQTT topic subscribed to receive sensor values. +The MQTT topic subscribed to receive sensor values. unit_of_measurement *Optional @@ -38,17 +41,18 @@ DEFAULT_NAME = "MQTT Sensor" DEPENDENCIES = ['mqtt'] + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Add MQTT Sensor """ - + if config.get('state_topic') is None: _LOGGER.error("Missing required variable: state_topic") return False - + add_devices_callback([MqttSensor( hass, - config.get('name',DEFAULT_NAME), + config.get('name', DEFAULT_NAME), config.get('state_topic'), config.get('unit_of_measurement'))]) @@ -86,4 +90,4 @@ class MqttSensor(Entity): @property def state(self): """ Returns the state of the entity. """ - return self._state + return self._state From 8a63325abe9d5c0617cc53f1a2e16c27bcc67225 Mon Sep 17 00:00:00 2001 From: sfam Date: Thu, 20 Aug 2015 23:09:11 +0100 Subject: [PATCH 4/8] update --- homeassistant/components/sensor/mqtt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 1af8614a7dc..b6c7a7b4ea1 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -4,9 +4,9 @@ homeassistant.components.sensor.mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a MQTT sensor. -This generic sensor implementation uses the MQTT message payload +This generic sensor implementation uses the MQTT message payload as the sensor value. If messages in this state_topic are published -with RETAIN flag, the sensor will receive an instant update with +with RETAIN flag, the sensor will receive an instant update with last known value. Otherwise, the initial state will be undefined. sensor: From 01ed3b18cc8c28038e5df8760eb141748538d587 Mon Sep 17 00:00:00 2001 From: sfam Date: Sat, 22 Aug 2015 14:57:57 +0100 Subject: [PATCH 5/8] update docstring --- homeassistant/components/sensor/mqtt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index b6c7a7b4ea1..d5dc192e450 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -67,6 +67,7 @@ class MqttSensor(Entity): self._unit_of_measurement = unit_of_measurement def message_received(topic, payload, qos): + """ A new MQTT message has been received. """ self._state = payload self.update_ha_state() From a9a650edb6a86fcce54c7203b54deb9d793438c6 Mon Sep 17 00:00:00 2001 From: sfam Date: Sat, 22 Aug 2015 15:10:49 +0100 Subject: [PATCH 6/8] update style issues --- homeassistant/components/switch/mqtt.py | 44 ++++++++++++++----------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index ee38c5fbb43..09904e1d52a 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -4,16 +4,18 @@ homeassistant.components.switch.mqtt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Allows to configure a MQTT switch. -In an ideal scenario, the MQTT device will have a state topic to publish state changes. -If these messages are published with RETAIN flag, the MQTT switch will receive -an instant state update after subscription and will start with correct state. Otherwise, -the initial state of the switch will be false/off. +In an ideal scenario, the MQTT device will have a state topic to publish +state changes. If these messages are published with RETAIN flag, the MQTT +switch will receive an instant state update after subscription and will +start with correct state. Otherwise, the initial state of the switch will +be false/off. -When a state topic is not available, the switch will work in an optimistic mode. -In this mode, the MQTT switch will immediately change state after every command. -Otherwise, the switch will wait for state confirmation from device (message from state_topic). +When a state topic is not available, the switch will work in optimistic mode. +In this mode, the switch will immediately change state after every command. +Otherwise, the switch will wait for state confirmation from device +(message from state_topic). -Optimistic mode can be forced, even if state topic is available. +Optimistic mode can be forced, even if state topic is available. Try to enable it, if experiencing incorrect switch operation. @@ -36,7 +38,7 @@ The name of the switch. Default is 'MQTT Switch'. state_topic *Optional -The MQTT topic subscribed to receive state updates. +The MQTT topic subscribed to receive state updates. If not specified, optimistic mode will be forced. command_topic @@ -70,6 +72,7 @@ DEFAULT_OPTIMISTIC = False DEPENDENCIES = ['mqtt'] + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices_callback, discovery_info=None): """ Add MQTT Switch """ @@ -77,7 +80,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): if config.get('command_topic') is None: _LOGGER.error("Missing required variable: command_topic") return False - + add_devices_callback([MqttSwitch( hass, config.get('name', DEFAULT_NAME), @@ -87,10 +90,12 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('payload_off', DEFAULT_PAYLOAD_OFF), config.get('optimistic', DEFAULT_OPTIMISTIC))]) + class MqttSwitch(SwitchDevice): """ Represents a switch that can be togggled using MQTT """ - def __init__(self, hass, name, state_topic, command_topic, payload_on, payload_off, optimistic): - self._state = False + def __init__(self, hass, name, state_topic, command_topic, + payload_on, payload_off, optimistic): + self._state = False self._hass = hass self._name = name self._state_topic = state_topic @@ -98,20 +103,21 @@ class MqttSwitch(SwitchDevice): self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic - + def message_received(topic, payload, qos): - if (payload == self._payload_on): + """ A new MQTT message has been received. """ + if payload == self._payload_on: self._state = True self.update_ha_state() - elif (payload == self._payload_off): + elif payload == self._payload_off: self._state = False self.update_ha_state() if self._state_topic is None: - """ force optimistic mode """ + # force optimistic mode self._optimistic = True else: - """ subscribe the state_topic """ + # subscribe the state_topic mqtt.subscribe(hass, self._state_topic, message_received) @property @@ -133,7 +139,7 @@ class MqttSwitch(SwitchDevice): """ Turn the device on. """ mqtt.publish(self.hass, self._command_topic, self._payload_on) if self._optimistic: - """ optimistically assume that switch has changed state """ + # optimistically assume that switch has changed state self._state = True self.update_ha_state() @@ -141,6 +147,6 @@ class MqttSwitch(SwitchDevice): """ Turn the device off. """ mqtt.publish(self.hass, self._command_topic, self._payload_off) if self._optimistic: - """ optimistically assume that switch has changed state """ + # optimistically assume that switch has changed state self._state = False self.update_ha_state() From fd7808e6f4d6127c3ef218d15a2c56066ba531e0 Mon Sep 17 00:00:00 2001 From: sfam Date: Sat, 22 Aug 2015 16:04:55 +0100 Subject: [PATCH 7/8] update --- homeassistant/components/switch/mqtt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index 09904e1d52a..2d547b1bb69 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -6,7 +6,7 @@ Allows to configure a MQTT switch. In an ideal scenario, the MQTT device will have a state topic to publish state changes. If these messages are published with RETAIN flag, the MQTT -switch will receive an instant state update after subscription and will +switch will receive an instant state update after subscription and will start with correct state. Otherwise, the initial state of the switch will be false/off. @@ -91,6 +91,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('optimistic', DEFAULT_OPTIMISTIC))]) +#pylint: disable-msg=too-many-arguments class MqttSwitch(SwitchDevice): """ Represents a switch that can be togggled using MQTT """ def __init__(self, hass, name, state_topic, command_topic, From d0c674b756c6cca5e4b95b856c56c7d8c0f47d91 Mon Sep 17 00:00:00 2001 From: sfam Date: Sat, 22 Aug 2015 16:10:29 +0100 Subject: [PATCH 8/8] update --- homeassistant/components/switch/mqtt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index 2d547b1bb69..c6ebdaa2ad6 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -91,7 +91,7 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('optimistic', DEFAULT_OPTIMISTIC))]) -#pylint: disable-msg=too-many-arguments +# pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSwitch(SwitchDevice): """ Represents a switch that can be togggled using MQTT """ def __init__(self, hass, name, state_topic, command_topic,