diff --git a/.coveragerc b/.coveragerc index 79b71dda807..b2acc2d5657 100644 --- a/.coveragerc +++ b/.coveragerc @@ -96,7 +96,7 @@ omit = homeassistant/components/*/homematic.py homeassistant/components/pilight.py - homeassistant/components/*/pilight.py + homeassistant/components/switch/pilight.py homeassistant/components/knx.py homeassistant/components/*/knx.py diff --git a/homeassistant/components/sensor/pilight.py b/homeassistant/components/sensor/pilight.py new file mode 100644 index 00000000000..99caebd708c --- /dev/null +++ b/homeassistant/components/sensor/pilight.py @@ -0,0 +1,96 @@ +""" +Support for pilight sensors. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.pilight/ +""" +import logging + +import voluptuous as vol + +from homeassistant.const import ( + CONF_NAME, STATE_UNKNOWN, CONF_UNIT_OF_MEASUREMENT, + CONF_PAYLOAD) +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.helpers.entity import Entity +import homeassistant.components.pilight as pilight +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Pilight Sensor' +DEPENDENCIES = ['pilight'] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required("variable"): cv.string, + vol.Required(CONF_PAYLOAD): vol.Schema(dict), + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_UNIT_OF_MEASUREMENT, default=None): cv.string, +}) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup pilight Sensor.""" + add_devices([PilightSensor( + hass=hass, + name=config.get(CONF_NAME), + variable=config.get("variable"), + payload=config.get(CONF_PAYLOAD), + unit_of_measurement=config.get(CONF_UNIT_OF_MEASUREMENT) + )]) + + +# pylint: disable=too-many-arguments, too-many-instance-attributes +class PilightSensor(Entity): + """Representation of a sensor that can be updated using pilight.""" + + def __init__(self, hass, name, variable, payload, unit_of_measurement): + """Initialize the sensor.""" + self._state = STATE_UNKNOWN + self._hass = hass + self._name = name + self._variable = variable + self._payload = payload + self._unit_of_measurement = unit_of_measurement + + hass.bus.listen(pilight.EVENT, self._handle_code) + + @property + def should_poll(self): + """No polling needed.""" + return False + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def unit_of_measurement(self): + """Return the unit this state is expressed in.""" + return self._unit_of_measurement + + @property + def state(self): + """Return the state of the entity.""" + return self._state + + def _handle_code(self, call): + """Handle received code by the pilight-daemon. + + If the code matches the defined playload + of this sensor the sensor state is changed accordingly. + """ + # Check if received code matches defined playoad + # True if payload is contained in received code dict, not + # all items have to match + if self._payload.items() <= call.data.items(): + try: + value = call.data[self._variable] + self._state = value + self.update_ha_state() + except KeyError: + _LOGGER.error( + 'No variable %s in received code data %s', + str(self._variable), str(call.data)) diff --git a/tests/components/sensor/test_pilight.py b/tests/components/sensor/test_pilight.py new file mode 100644 index 00000000000..c78c91545ab --- /dev/null +++ b/tests/components/sensor/test_pilight.py @@ -0,0 +1,120 @@ +"""The tests for the Pilight sensor platform.""" +import logging + +from homeassistant.bootstrap import setup_component +import homeassistant.components.sensor as sensor +from homeassistant.components import pilight + +from tests.common import get_test_home_assistant, assert_setup_component + +HASS = None + + +def fire_pilight_message(protocol, data): + """Fire the fake pilight message.""" + message = {pilight.ATTR_PROTOCOL: protocol} + message.update(data) + HASS.bus.fire(pilight.EVENT, message) + + +def setup_function(): # pylint: disable=invalid-name + """Initialize a Home Assistant server.""" + global HASS + + HASS = get_test_home_assistant() + HASS.config.components = ['pilight'] + + +def teardown_function(): # pylint: disable=invalid-name + """Stop the Home Assistant server.""" + HASS.stop() + + +def test_sensor_value_from_code(): + """Test the setting of value via pilight.""" + with assert_setup_component(1): + setup_component(HASS, sensor.DOMAIN, { + sensor.DOMAIN: { + 'platform': 'pilight', + 'name': 'test', + 'variable': 'test', + 'payload': {'protocol': 'test-protocol'}, + 'unit_of_measurement': 'fav unit' + } + }) + + state = HASS.states.get('sensor.test') + assert state.state == 'unknown' + + unit_of_measurement = state.attributes.get('unit_of_measurement') + assert unit_of_measurement == 'fav unit' + + # Set value from data with correct payload + fire_pilight_message(protocol='test-protocol', + data={'test': 42}) + HASS.block_till_done() + state = HASS.states.get('sensor.test') + assert state.state == '42' + + +def test_disregard_wrong_payload(): + """Test omitting setting of value with wrong payload.""" + with assert_setup_component(1): + setup_component(HASS, sensor.DOMAIN, { + sensor.DOMAIN: { + 'platform': 'pilight', + 'name': 'test_2', + 'variable': 'test', + 'payload': {'uuid': '1-2-3-4', + 'protocol': 'test-protocol_2'} + } + }) + + # Try set value from data with incorrect payload + fire_pilight_message(protocol='test-protocol_2', + data={'test': 'data', + 'uuid': '0-0-0-0'}) + HASS.block_till_done() + state = HASS.states.get('sensor.test_2') + assert state.state == 'unknown' + + # Try set value from data with partially matched payload + fire_pilight_message(protocol='wrong-protocol', + data={'test': 'data', + 'uuid': '1-2-3-4'}) + HASS.block_till_done() + state = HASS.states.get('sensor.test_2') + assert state.state == 'unknown' + + # Try set value from data with fully matched payload + fire_pilight_message(protocol='test-protocol_2', + data={'test': 'data', + 'uuid': '1-2-3-4', + 'other_payload': 3.141}) + HASS.block_till_done() + state = HASS.states.get('sensor.test_2') + assert state.state == 'data' + + +def test_variable_missing(caplog): + """Check if error message when variable missing.""" + caplog.set_level(logging.ERROR) + with assert_setup_component(1): + setup_component(HASS, sensor.DOMAIN, { + sensor.DOMAIN: { + 'platform': 'pilight', + 'name': 'test_3', + 'variable': 'test', + 'payload': {'protocol': 'test-protocol'} + } + }) + + # Create code without sensor variable + fire_pilight_message(protocol='test-protocol', + data={'uuid': '1-2-3-4', + 'other_variable': 3.141}) + HASS.block_till_done() + + logs = caplog.text + + assert 'No variable test in received code' in logs