diff --git a/homeassistant/components/freedompro/__init__.py b/homeassistant/components/freedompro/__init__.py index 6e5dd438e2d..ddb6688a127 100644 --- a/homeassistant/components/freedompro/__init__.py +++ b/homeassistant/components/freedompro/__init__.py @@ -14,7 +14,7 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) -PLATFORMS = ["light", "switch"] +PLATFORMS = ["light", "sensor", "switch"] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): diff --git a/homeassistant/components/freedompro/sensor.py b/homeassistant/components/freedompro/sensor.py new file mode 100644 index 00000000000..00dbb30570c --- /dev/null +++ b/homeassistant/components/freedompro/sensor.py @@ -0,0 +1,95 @@ +"""Support for Freedompro sensor.""" +from homeassistant.components.sensor import ( + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + SensorEntity, +) +from homeassistant.const import CONF_API_KEY, LIGHT_LUX, PERCENTAGE, TEMP_CELSIUS +from homeassistant.core import callback +from homeassistant.helpers import aiohttp_client +from homeassistant.helpers.update_coordinator import CoordinatorEntity + +from .const import DOMAIN + +DEVICE_CLASS_MAP = { + "temperatureSensor": DEVICE_CLASS_TEMPERATURE, + "humiditySensor": DEVICE_CLASS_HUMIDITY, + "lightSensor": DEVICE_CLASS_ILLUMINANCE, +} +STATE_CLASS_MAP = { + "temperatureSensor": STATE_CLASS_MEASUREMENT, + "humiditySensor": STATE_CLASS_MEASUREMENT, + "lightSensor": None, +} +UNIT_MAP = { + "temperatureSensor": TEMP_CELSIUS, + "humiditySensor": PERCENTAGE, + "lightSensor": LIGHT_LUX, +} +DEVICE_KEY_MAP = { + "temperatureSensor": "currentTemperature", + "humiditySensor": "currentRelativeHumidity", + "lightSensor": "currentAmbientLightLevel", +} +SUPPORTED_SENSORS = {"temperatureSensor", "humiditySensor", "lightSensor"} + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up Freedompro sensor.""" + api_key = entry.data[CONF_API_KEY] + coordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities( + Device(hass, api_key, device, coordinator) + for device in coordinator.data + if device["type"] in SUPPORTED_SENSORS + ) + + +class Device(CoordinatorEntity, SensorEntity): + """Representation of an Freedompro sensor.""" + + def __init__(self, hass, api_key, device, coordinator): + """Initialize the Freedompro sensor.""" + super().__init__(coordinator) + self._hass = hass + self._session = aiohttp_client.async_get_clientsession(self._hass) + self._api_key = api_key + self._attr_name = device["name"] + self._attr_unique_id = device["uid"] + self._type = device["type"] + self._characteristics = device["characteristics"] + self._attr_device_info = { + "name": self.name, + "identifiers": { + (DOMAIN, self.unique_id), + }, + "model": self._type, + "manufacturer": "Freedompro", + } + self._attr_device_class = DEVICE_CLASS_MAP[self._type] + self._attr_state_class = STATE_CLASS_MAP[self._type] + self._attr_unit_of_measurement = UNIT_MAP[self._type] + self._attr_state = 0 + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + device = next( + ( + device + for device in self.coordinator.data + if device["uid"] == self.unique_id + ), + None, + ) + if device is not None and "state" in device: + state = device["state"] + self._attr_state = state[DEVICE_KEY_MAP[self._type]] + super()._handle_coordinator_update() + + async def async_added_to_hass(self) -> None: + """When entity is added to hass.""" + await super().async_added_to_hass() + self._handle_coordinator_update() diff --git a/tests/components/freedompro/const.py b/tests/components/freedompro/const.py index 0e8f5c4aa52..0db9d5884b1 100644 --- a/tests/components/freedompro/const.py +++ b/tests/components/freedompro/const.py @@ -207,7 +207,7 @@ DEVICES_STATE = [ { "uid": "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JVRAR_6WVL1Y0PJ5GFWGPMFV7FLVD4MZKBWXC_UFWYM", "type": "lightSensor", - "state": {"currentAmbientLightLevel": 500}, + "state": {"currentAmbientLightLevel": 0}, "online": True, }, { diff --git a/tests/components/freedompro/test_sensor.py b/tests/components/freedompro/test_sensor.py new file mode 100644 index 00000000000..b6f809569f1 --- /dev/null +++ b/tests/components/freedompro/test_sensor.py @@ -0,0 +1,76 @@ +"""Tests for the Freedompro sensor.""" +from datetime import timedelta +from unittest.mock import patch + +import pytest + +from homeassistant.helpers import entity_registry as er +from homeassistant.util.dt import utcnow + +from tests.common import async_fire_time_changed +from tests.components.freedompro.const import DEVICES_STATE + + +@pytest.mark.parametrize( + "entity_id, uid, name", + [ + ( + "sensor.garden_humidity_sensor", + "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*QN-DDFMPEPRDOQV7W7JQG3NL0NPZGTLIBYT3HFSPNEY", + "Garden humidity sensor", + ), + ( + "sensor.living_room_temperature_sensor", + "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*LWPVY7X1AX0DRWLYUUNZ3ZSTHMYNDDBQTPZCZQUUASA", + "Living room temperature sensor", + ), + ( + "sensor.garden_light_sensors", + "3WRRJR6RCZQZSND8VP0YTO3YXCSOFPKBMW8T51TU-LQ*JVRAR_6WVL1Y0PJ5GFWGPMFV7FLVD4MZKBWXC_UFWYM", + "Garden light sensors", + ), + ], +) +async def test_sensor_get_state( + hass, init_integration, entity_id: str, uid: str, name: str +): + """Test states of the sensor.""" + init_integration + registry = er.async_get(hass) + + state = hass.states.get(entity_id) + assert state + assert state.attributes.get("friendly_name") == name + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == uid + + assert state.state == "0" + + get_states_response = list(DEVICES_STATE) + for state_response in get_states_response: + if state_response["uid"] == uid: + if state_response["type"] == "lightSensor": + state_response["state"]["currentAmbientLightLevel"] = "1" + if state_response["type"] == "temperatureSensor": + state_response["state"]["currentTemperature"] = "1" + if state_response["type"] == "humiditySensor": + state_response["state"]["currentRelativeHumidity"] = "1" + with patch( + "homeassistant.components.freedompro.get_states", + return_value=get_states_response, + ): + + async_fire_time_changed(hass, utcnow() + timedelta(hours=2)) + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state + assert state.attributes.get("friendly_name") == name + + entry = registry.async_get(entity_id) + assert entry + assert entry.unique_id == uid + + assert state.state == "1"