diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py new file mode 100644 index 00000000000..d5dc192e450 --- /dev/null +++ b/homeassistant/components/sensor/mqtt.py @@ -0,0 +1,94 @@ +# -*- 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. 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 + 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): + """ A new MQTT message has been received. """ + 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 diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py new file mode 100644 index 00000000000..c6ebdaa2ad6 --- /dev/null +++ b/homeassistant/components/switch/mqtt.py @@ -0,0 +1,153 @@ +# -*- 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 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. +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))]) + + +# 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, + 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): + """ A new MQTT message has been received. """ + 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()