diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index cd5b5370175..9ec5169c729 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -9,6 +9,7 @@ https://home-assistant.io/components/mqtt/ import logging import os import socket +import json from homeassistant.exceptions import HomeAssistantError import homeassistant.util as util @@ -30,7 +31,7 @@ SERVICE_PUBLISH = 'publish' EVENT_MQTT_MESSAGE_RECEIVED = 'MQTT_MESSAGE_RECEIVED' DEPENDENCIES = [] -REQUIREMENTS = ['paho-mqtt==1.1'] +REQUIREMENTS = ['paho-mqtt==1.1', 'jsonpath-rw==1.4.0'] CONF_BROKER = 'broker' CONF_PORT = 'port' @@ -127,6 +128,31 @@ def setup(hass, config): return True +# pylint: disable=too-few-public-methods +class _JsonFmtParser(object): + """ Implements a json parser on xpath""" + def __init__(self, jsonpath): + import jsonpath_rw + self._expr = jsonpath_rw.parse(jsonpath) + + def __call__(self, payload): + match = self._expr.find(json.loads(payload)) + return match[0].value if len(match) > 0 else payload + + +# pylint: disable=too-few-public-methods +class FmtParser(object): + """ wrapper for all supported formats """ + def __init__(self, fmt): + self._parse = lambda x: x + if fmt: + if fmt.startswith('json:'): + self._parse = _JsonFmtParser(fmt[5:]) + + def __call__(self, payload): + return self._parse(payload) + + # This is based on one of the paho-mqtt examples: # http://git.eclipse.org/c/paho/org.eclipse.paho.mqtt.python.git/tree/examples/sub-class.py # pylint: disable=too-many-arguments diff --git a/homeassistant/components/sensor/mqtt.py b/homeassistant/components/sensor/mqtt.py index 2623d2fdcce..2bbed97e40c 100644 --- a/homeassistant/components/sensor/mqtt.py +++ b/homeassistant/components/sensor/mqtt.py @@ -31,23 +31,26 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('name', DEFAULT_NAME), config.get('state_topic'), config.get('qos', DEFAULT_QOS), - config.get('unit_of_measurement'))]) + config.get('unit_of_measurement'), + config.get('state_format'))]) # pylint: disable=too-many-arguments, too-many-instance-attributes class MqttSensor(Entity): """ Represents a sensor that can be updated using MQTT. """ - def __init__(self, hass, name, state_topic, qos, unit_of_measurement): + def __init__(self, hass, name, state_topic, qos, unit_of_measurement, + state_format): self._state = "-" self._hass = hass self._name = name self._state_topic = state_topic self._qos = qos self._unit_of_measurement = unit_of_measurement + self._parse = mqtt.FmtParser(state_format) def message_received(topic, payload, qos): """ A new MQTT message has been received. """ - self._state = payload + self._state = self._parse(payload) self.update_ha_state() mqtt.subscribe(hass, self._state_topic, message_received, self._qos) diff --git a/homeassistant/components/switch/mqtt.py b/homeassistant/components/switch/mqtt.py index 12d3f486323..ed99a87868a 100644 --- a/homeassistant/components/switch/mqtt.py +++ b/homeassistant/components/switch/mqtt.py @@ -37,14 +37,15 @@ def setup_platform(hass, config, add_devices_callback, discovery_info=None): config.get('qos', DEFAULT_QOS), config.get('payload_on', DEFAULT_PAYLOAD_ON), config.get('payload_off', DEFAULT_PAYLOAD_OFF), - config.get('optimistic', DEFAULT_OPTIMISTIC))]) + config.get('optimistic', DEFAULT_OPTIMISTIC), + config.get('state_format'))]) # 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, qos, - payload_on, payload_off, optimistic): + payload_on, payload_off, optimistic, state_format): self._state = False self._hass = hass self._name = name @@ -54,9 +55,11 @@ class MqttSwitch(SwitchDevice): self._payload_on = payload_on self._payload_off = payload_off self._optimistic = optimistic + self._parse = mqtt.FmtParser(state_format) def message_received(topic, payload, qos): """ A new MQTT message has been received. """ + payload = self._parse(payload) if payload == self._payload_on: self._state = True self.update_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index 233f0583914..a9f24cdb65f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -75,6 +75,7 @@ https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b6 # homeassistant.components.mqtt paho-mqtt==1.1 +jsonpath-rw==1.4.0 # homeassistant.components.notify.pushbullet pushbullet.py==0.9.0 diff --git a/tests/components/sensor/test_mqtt.py b/tests/components/sensor/test_mqtt.py index b59ea867c58..0c17b95e212 100644 --- a/tests/components/sensor/test_mqtt.py +++ b/tests/components/sensor/test_mqtt.py @@ -39,3 +39,21 @@ class TestSensorMQTT(unittest.TestCase): self.assertEqual('100', state.state) self.assertEqual('fav unit', state.attributes.get('unit_of_measurement')) + + def test_setting_sensor_value_via_mqtt_json_message(self): + self.assertTrue(sensor.setup(self.hass, { + 'sensor': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'test-topic', + 'unit_of_measurement': 'fav unit', + 'state_format': 'json:val' + } + })) + + fire_mqtt_message(self.hass, 'test-topic', '{ "val": "100" }') + self.hass.pool.block_till_done() + state = self.hass.states.get('sensor.test') + + self.assertEqual('100', state.state) + diff --git a/tests/components/switch/test_mqtt.py b/tests/components/switch/test_mqtt.py index a09fcf86c58..4754e64e575 100644 --- a/tests/components/switch/test_mqtt.py +++ b/tests/components/switch/test_mqtt.py @@ -80,3 +80,31 @@ class TestSensorMQTT(unittest.TestCase): self.mock_publish.mock_calls[-1][1]) state = self.hass.states.get('switch.test') self.assertEqual(STATE_OFF, state.state) + + def test_controlling_state_via_topic_and_json_message(self): + self.assertTrue(switch.setup(self.hass, { + 'switch': { + 'platform': 'mqtt', + 'name': 'test', + 'state_topic': 'state-topic', + 'command_topic': 'command-topic', + 'payload_on': 'beer on', + 'payload_off': 'beer off', + 'state_format': 'json:val' + } + })) + + state = self.hass.states.get('switch.test') + self.assertEqual(STATE_OFF, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '{"val":"beer on"}') + self.hass.pool.block_till_done() + + state = self.hass.states.get('switch.test') + self.assertEqual(STATE_ON, state.state) + + fire_mqtt_message(self.hass, 'state-topic', '{"val":"beer off"}') + self.hass.pool.block_till_done() + + state = self.hass.states.get('switch.test') + self.assertEqual(STATE_OFF, state.state) \ No newline at end of file