diff --git a/homeassistant/components/canary.py b/homeassistant/components/canary.py index 8ab7218e201..4d45f31ae59 100644 --- a/homeassistant/components/canary.py +++ b/homeassistant/components/canary.py @@ -111,6 +111,13 @@ class CanaryData(object): """Return a list of readings based on device_id.""" return self._readings_by_device_id.get(device_id, []) + def get_reading(self, device_id, sensor_type): + """Return reading for device_id and sensor type.""" + readings = self._readings_by_device_id.get(device_id, []) + return next(( + reading.value for reading in readings + if reading.sensor_type == sensor_type), None) + def set_location_mode(self, location_id, mode_name, is_private=False): """Set location mode.""" self._api.set_location_mode(location_id, mode_name, is_private) diff --git a/homeassistant/components/sensor/canary.py b/homeassistant/components/sensor/canary.py index b0d2c27ae5d..56da1c4deea 100644 --- a/homeassistant/components/sensor/canary.py +++ b/homeassistant/components/sensor/canary.py @@ -4,13 +4,27 @@ Support for Canary sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.canary/ """ + from homeassistant.components.canary import DATA_CANARY -from homeassistant.const import TEMP_FAHRENHEIT, TEMP_CELSIUS +from homeassistant.const import TEMP_CELSIUS from homeassistant.helpers.entity import Entity DEPENDENCIES = ['canary'] -SENSOR_VALUE_PRECISION = 1 +SENSOR_VALUE_PRECISION = 2 +ATTR_AIR_QUALITY = "air_quality" + +# Sensor types are defined like so: +# sensor type name, unit_of_measurement, icon +SENSOR_TYPES = [ + ["temperature", TEMP_CELSIUS, "mdi:thermometer"], + ["humidity", "%", "mdi:water-percent"], + ["air_quality", None, "mdi:weather-windy"], +] + +STATE_AIR_QUALITY_NORMAL = "normal" +STATE_AIR_QUALITY_ABNORMAL = "abnormal" +STATE_AIR_QUALITY_VERY_ABNORMAL = "very_abnormal" def setup_platform(hass, config, add_devices, discovery_info=None): @@ -18,11 +32,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): data = hass.data[DATA_CANARY] devices = [] - from canary.api import SensorType for location in data.locations: for device in location.devices: if device.is_online: - for sensor_type in SensorType: + for sensor_type in SENSOR_TYPES: devices.append(CanarySensor(data, sensor_type, location, device)) @@ -37,10 +50,9 @@ class CanarySensor(Entity): self._data = data self._sensor_type = sensor_type self._device_id = device.device_id - self._is_celsius = location.is_celsius self._sensor_value = None - sensor_type_name = sensor_type.value.replace("_", " ").title() + sensor_type_name = sensor_type[0].replace("_", " ").title() self._name = '{} {} {}'.format(location.name, device.name, sensor_type_name) @@ -59,27 +71,51 @@ class CanarySensor(Entity): def unique_id(self): """Return the unique ID of this sensor.""" return "sensor_canary_{}_{}".format(self._device_id, - self._sensor_type.value) + self._sensor_type[0]) @property def unit_of_measurement(self): - """Return the unit of measurement this sensor expresses itself in.""" - from canary.api import SensorType - if self._sensor_type == SensorType.TEMPERATURE: - return TEMP_CELSIUS if self._is_celsius else TEMP_FAHRENHEIT - elif self._sensor_type == SensorType.HUMIDITY: - return "%" - elif self._sensor_type == SensorType.AIR_QUALITY: - return "" + """Return the unit of measurement.""" + return self._sensor_type[1] + + @property + def icon(self): + """Icon for the sensor.""" + return self._sensor_type[2] + + @property + def device_state_attributes(self): + """Return the state attributes.""" + if self._sensor_type[0] == "air_quality" \ + and self._sensor_value is not None: + air_quality = None + if self._sensor_value <= .4: + air_quality = STATE_AIR_QUALITY_VERY_ABNORMAL + elif self._sensor_value <= .59: + air_quality = STATE_AIR_QUALITY_ABNORMAL + elif self._sensor_value <= 1.0: + air_quality = STATE_AIR_QUALITY_NORMAL + + return { + ATTR_AIR_QUALITY: air_quality + } + return None def update(self): """Get the latest state of the sensor.""" self._data.update() - readings = self._data.get_readings(self._device_id) - value = next(( - reading.value for reading in readings - if reading.sensor_type == self._sensor_type), None) + from canary.api import SensorType + canary_sensor_type = None + if self._sensor_type[0] == "air_quality": + canary_sensor_type = SensorType.AIR_QUALITY + elif self._sensor_type[0] == "temperature": + canary_sensor_type = SensorType.TEMPERATURE + elif self._sensor_type[0] == "humidity": + canary_sensor_type = SensorType.HUMIDITY + + value = self._data.get_reading(self._device_id, canary_sensor_type) + if value is not None: self._sensor_value = round(float(value), SENSOR_VALUE_PRECISION) diff --git a/tests/components/sensor/test_canary.py b/tests/components/sensor/test_canary.py index b35b5630d60..79e2bf4ee35 100644 --- a/tests/components/sensor/test_canary.py +++ b/tests/components/sensor/test_canary.py @@ -3,13 +3,13 @@ import copy import unittest from unittest.mock import Mock -from canary.api import SensorType from homeassistant.components.canary import DATA_CANARY from homeassistant.components.sensor import canary -from homeassistant.components.sensor.canary import CanarySensor +from homeassistant.components.sensor.canary import CanarySensor, \ + SENSOR_TYPES, ATTR_AIR_QUALITY, STATE_AIR_QUALITY_NORMAL, \ + STATE_AIR_QUALITY_ABNORMAL, STATE_AIR_QUALITY_VERY_ABNORMAL from tests.common import (get_test_home_assistant) -from tests.components.test_canary import mock_device, mock_reading, \ - mock_location +from tests.components.test_canary import mock_device, mock_location VALID_CONFIG = { "canary": { @@ -55,38 +55,33 @@ class TestCanarySensorSetup(unittest.TestCase): self.assertEqual(6, len(self.DEVICES)) - def test_celsius_temperature_sensor(self): - """Test temperature sensor with celsius.""" - device = mock_device(10, "Family Room") - location = mock_location("Home", True) - - data = Mock() - data.get_readings.return_value = [ - mock_reading(SensorType.TEMPERATURE, 21.1234)] - - sensor = CanarySensor(data, SensorType.TEMPERATURE, location, device) - sensor.update() - - self.assertEqual("Home Family Room Temperature", sensor.name) - self.assertEqual("sensor_canary_10_temperature", sensor.unique_id) - self.assertEqual("°C", sensor.unit_of_measurement) - self.assertEqual(21.1, sensor.state) - - def test_fahrenheit_temperature_sensor(self): + def test_temperature_sensor(self): """Test temperature sensor with fahrenheit.""" device = mock_device(10, "Family Room") location = mock_location("Home", False) data = Mock() - data.get_readings.return_value = [ - mock_reading(SensorType.TEMPERATURE, 21.1567)] + data.get_reading.return_value = 21.1234 - sensor = CanarySensor(data, SensorType.TEMPERATURE, location, device) + sensor = CanarySensor(data, SENSOR_TYPES[0], location, device) sensor.update() self.assertEqual("Home Family Room Temperature", sensor.name) - self.assertEqual("°F", sensor.unit_of_measurement) - self.assertEqual(21.2, sensor.state) + self.assertEqual("°C", sensor.unit_of_measurement) + self.assertEqual(21.12, sensor.state) + + def test_temperature_sensor_with_none_sensor_value(self): + """Test temperature sensor with fahrenheit.""" + device = mock_device(10, "Family Room") + location = mock_location("Home", False) + + data = Mock() + data.get_reading.return_value = None + + sensor = CanarySensor(data, SENSOR_TYPES[0], location, device) + sensor.update() + + self.assertEqual(None, sensor.state) def test_humidity_sensor(self): """Test humidity sensor.""" @@ -94,28 +89,79 @@ class TestCanarySensorSetup(unittest.TestCase): location = mock_location("Home") data = Mock() - data.get_readings.return_value = [ - mock_reading(SensorType.HUMIDITY, 50.4567)] + data.get_reading.return_value = 50.4567 - sensor = CanarySensor(data, SensorType.HUMIDITY, location, device) + sensor = CanarySensor(data, SENSOR_TYPES[1], location, device) sensor.update() self.assertEqual("Home Family Room Humidity", sensor.name) self.assertEqual("%", sensor.unit_of_measurement) - self.assertEqual(50.5, sensor.state) + self.assertEqual(50.46, sensor.state) - def test_air_quality_sensor(self): + def test_air_quality_sensor_with_very_abnormal_reading(self): """Test air quality sensor.""" device = mock_device(10, "Family Room") location = mock_location("Home") data = Mock() - data.get_readings.return_value = [ - mock_reading(SensorType.AIR_QUALITY, 50.4567)] + data.get_reading.return_value = 0.4 - sensor = CanarySensor(data, SensorType.AIR_QUALITY, location, device) + sensor = CanarySensor(data, SENSOR_TYPES[2], location, device) sensor.update() self.assertEqual("Home Family Room Air Quality", sensor.name) - self.assertEqual("", sensor.unit_of_measurement) - self.assertEqual(50.5, sensor.state) + self.assertEqual(None, sensor.unit_of_measurement) + self.assertEqual(0.4, sensor.state) + + air_quality = sensor.device_state_attributes[ATTR_AIR_QUALITY] + self.assertEqual(STATE_AIR_QUALITY_VERY_ABNORMAL, air_quality) + + def test_air_quality_sensor_with_abnormal_reading(self): + """Test air quality sensor.""" + device = mock_device(10, "Family Room") + location = mock_location("Home") + + data = Mock() + data.get_reading.return_value = 0.59 + + sensor = CanarySensor(data, SENSOR_TYPES[2], location, device) + sensor.update() + + self.assertEqual("Home Family Room Air Quality", sensor.name) + self.assertEqual(None, sensor.unit_of_measurement) + self.assertEqual(0.59, sensor.state) + + air_quality = sensor.device_state_attributes[ATTR_AIR_QUALITY] + self.assertEqual(STATE_AIR_QUALITY_ABNORMAL, air_quality) + + def test_air_quality_sensor_with_normal_reading(self): + """Test air quality sensor.""" + device = mock_device(10, "Family Room") + location = mock_location("Home") + + data = Mock() + data.get_reading.return_value = 1.0 + + sensor = CanarySensor(data, SENSOR_TYPES[2], location, device) + sensor.update() + + self.assertEqual("Home Family Room Air Quality", sensor.name) + self.assertEqual(None, sensor.unit_of_measurement) + self.assertEqual(1.0, sensor.state) + + air_quality = sensor.device_state_attributes[ATTR_AIR_QUALITY] + self.assertEqual(STATE_AIR_QUALITY_NORMAL, air_quality) + + def test_air_quality_sensor_with_none_sensor_value(self): + """Test air quality sensor.""" + device = mock_device(10, "Family Room") + location = mock_location("Home") + + data = Mock() + data.get_reading.return_value = None + + sensor = CanarySensor(data, SENSOR_TYPES[2], location, device) + sensor.update() + + self.assertEqual(None, sensor.state) + self.assertEqual(None, sensor.device_state_attributes)