From ae6c394b538ff05a01eb70d96e65a3abf27de135 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Wed, 29 May 2024 08:34:00 +0200 Subject: [PATCH] Add smoke detector temperature to Yale Smart Alarm (#116306) --- .../components/yale_smart_alarm/const.py | 1 + .../yale_smart_alarm/coordinator.py | 6 + .../components/yale_smart_alarm/sensor.py | 39 ++++++ .../yale_smart_alarm/fixtures/get_all.json | 112 +++++++++++++++++ .../snapshots/test_diagnostics.ambr | 118 ++++++++++++++++++ .../yale_smart_alarm/test_sensor.py | 21 ++++ 6 files changed, 297 insertions(+) create mode 100644 homeassistant/components/yale_smart_alarm/sensor.py create mode 100644 tests/components/yale_smart_alarm/test_sensor.py diff --git a/homeassistant/components/yale_smart_alarm/const.py b/homeassistant/components/yale_smart_alarm/const.py index 2582854a3bc..e7b732c6cf9 100644 --- a/homeassistant/components/yale_smart_alarm/const.py +++ b/homeassistant/components/yale_smart_alarm/const.py @@ -39,6 +39,7 @@ PLATFORMS = [ Platform.BINARY_SENSOR, Platform.BUTTON, Platform.LOCK, + Platform.SENSOR, ] STATE_MAP = { diff --git a/homeassistant/components/yale_smart_alarm/coordinator.py b/homeassistant/components/yale_smart_alarm/coordinator.py index 642704b637d..5307e166e17 100644 --- a/homeassistant/components/yale_smart_alarm/coordinator.py +++ b/homeassistant/components/yale_smart_alarm/coordinator.py @@ -39,6 +39,7 @@ class YaleDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): locks = [] door_windows = [] + temp_sensors = [] for device in updates["cycle"]["device_status"]: state = device["status1"] @@ -107,19 +108,24 @@ class YaleDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]): device["_state"] = "unavailable" door_windows.append(device) continue + if device["type"] == "device_type.temperature_sensor": + temp_sensors.append(device) _sensor_map = { contact["address"]: contact["_state"] for contact in door_windows } _lock_map = {lock["address"]: lock["_state"] for lock in locks} + _temp_map = {temp["address"]: temp["status_temp"] for temp in temp_sensors} return { "alarm": updates["arm_status"], "locks": locks, "door_windows": door_windows, + "temp_sensors": temp_sensors, "status": updates["status"], "online": updates["online"], "sensor_map": _sensor_map, + "temp_map": _temp_map, "lock_map": _lock_map, "panel_info": updates["panel_info"], } diff --git a/homeassistant/components/yale_smart_alarm/sensor.py b/homeassistant/components/yale_smart_alarm/sensor.py new file mode 100644 index 00000000000..50343f2e41f --- /dev/null +++ b/homeassistant/components/yale_smart_alarm/sensor.py @@ -0,0 +1,39 @@ +"""Sensors for Yale Alarm.""" + +from __future__ import annotations + +from typing import cast + +from homeassistant.components.sensor import SensorDeviceClass, SensorEntity +from homeassistant.const import UnitOfTemperature +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import StateType + +from . import YaleConfigEntry +from .entity import YaleEntity + + +async def async_setup_entry( + hass: HomeAssistant, entry: YaleConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the Yale sensor entry.""" + + coordinator = entry.runtime_data + + async_add_entities( + YaleTemperatureSensor(coordinator, data) + for data in coordinator.data["temp_sensors"] + ) + + +class YaleTemperatureSensor(YaleEntity, SensorEntity): + """Representation of a Yale temperature sensor.""" + + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + + @property + def native_value(self) -> StateType: + "Return native value." + return cast(float, self.coordinator.data["temp_map"][self._attr_unique_id]) diff --git a/tests/components/yale_smart_alarm/fixtures/get_all.json b/tests/components/yale_smart_alarm/fixtures/get_all.json index 0878cbf9c6a..e85a93f3c3e 100644 --- a/tests/components/yale_smart_alarm/fixtures/get_all.json +++ b/tests/components/yale_smart_alarm/fixtures/get_all.json @@ -503,6 +503,62 @@ "status_fault": [], "status_open": ["device_status.error"], "trigger_by_zone": [] + }, + { + "area": "1", + "no": "8", + "rf": null, + "address": "3456", + "type": "device_type.temperature_sensor", + "name": "Smoke alarm", + "status1": "", + "status2": null, + "status_switch": null, + "status_power": null, + "status_temp": 21, + "status_humi": null, + "status_dim_level": null, + "status_lux": "", + "status_hue": null, + "status_saturation": null, + "rssi": "9", + "mac": "00:00:00:00:1C", + "scene_trigger": "0", + "status_total_energy": null, + "device_id2": "", + "extension": null, + "minigw_protocol": "", + "minigw_syncing": "", + "minigw_configuration_data": "", + "minigw_product_data": "", + "minigw_lock_status": "", + "minigw_number_of_credentials_supported": "", + "sresp_button_3": null, + "sresp_button_1": null, + "sresp_button_2": null, + "sresp_button_4": null, + "ipcam_trigger_by_zone1": null, + "ipcam_trigger_by_zone2": null, + "ipcam_trigger_by_zone3": null, + "ipcam_trigger_by_zone4": null, + "scene_restore": null, + "thermo_mode": null, + "thermo_setpoint": null, + "thermo_c_setpoint": null, + "thermo_setpoint_away": null, + "thermo_c_setpoint_away": null, + "thermo_fan_mode": null, + "thermo_schd_setting": null, + "group_id": null, + "group_name": null, + "bypass": "0", + "device_id": "3456", + "status_temp_format": "C", + "type_no": "40", + "device_group": "001", + "status_fault": [], + "status_open": [], + "trigger_by_zone": [] } ], "MODE": [ @@ -1035,6 +1091,62 @@ "status_fault": [], "status_open": ["device_status.error"], "trigger_by_zone": [] + }, + { + "area": "1", + "no": "8", + "rf": null, + "address": "3456", + "type": "device_type.temperature_sensor", + "name": "Smoke alarm", + "status1": "", + "status2": null, + "status_switch": null, + "status_power": null, + "status_temp": 21, + "status_humi": null, + "status_dim_level": null, + "status_lux": "", + "status_hue": null, + "status_saturation": null, + "rssi": "9", + "mac": "00:00:00:00:1C", + "scene_trigger": "0", + "status_total_energy": null, + "device_id2": "", + "extension": null, + "minigw_protocol": "", + "minigw_syncing": "", + "minigw_configuration_data": "", + "minigw_product_data": "", + "minigw_lock_status": "", + "minigw_number_of_credentials_supported": "", + "sresp_button_3": null, + "sresp_button_1": null, + "sresp_button_2": null, + "sresp_button_4": null, + "ipcam_trigger_by_zone1": null, + "ipcam_trigger_by_zone2": null, + "ipcam_trigger_by_zone3": null, + "ipcam_trigger_by_zone4": null, + "scene_restore": null, + "thermo_mode": null, + "thermo_setpoint": null, + "thermo_c_setpoint": null, + "thermo_setpoint_away": null, + "thermo_c_setpoint_away": null, + "thermo_fan_mode": null, + "thermo_schd_setting": null, + "group_id": null, + "group_name": null, + "bypass": "0", + "device_id": "3456", + "status_temp_format": "C", + "type_no": "40", + "device_group": "001", + "status_fault": [], + "status_open": [], + "trigger_by_zone": [] } ], "capture_latest": null, diff --git a/tests/components/yale_smart_alarm/snapshots/test_diagnostics.ambr b/tests/components/yale_smart_alarm/snapshots/test_diagnostics.ambr index ae720a611e3..a5dfe4b50dd 100644 --- a/tests/components/yale_smart_alarm/snapshots/test_diagnostics.ambr +++ b/tests/components/yale_smart_alarm/snapshots/test_diagnostics.ambr @@ -572,6 +572,65 @@ 'type': 'device_type.door_lock', 'type_no': '72', }), + dict({ + 'address': '**REDACTED**', + 'area': '1', + 'bypass': '0', + 'device_group': '001', + 'device_id': '**REDACTED**', + 'device_id2': '', + 'extension': None, + 'group_id': None, + 'group_name': None, + 'ipcam_trigger_by_zone1': None, + 'ipcam_trigger_by_zone2': None, + 'ipcam_trigger_by_zone3': None, + 'ipcam_trigger_by_zone4': None, + 'mac': '**REDACTED**', + 'minigw_configuration_data': '', + 'minigw_lock_status': '', + 'minigw_number_of_credentials_supported': '', + 'minigw_product_data': '', + 'minigw_protocol': '', + 'minigw_syncing': '', + 'name': '**REDACTED**', + 'no': '8', + 'rf': None, + 'rssi': '9', + 'scene_restore': None, + 'scene_trigger': '0', + 'sresp_button_1': None, + 'sresp_button_2': None, + 'sresp_button_3': None, + 'sresp_button_4': None, + 'status1': '', + 'status2': None, + 'status_dim_level': None, + 'status_fault': list([ + ]), + 'status_hue': None, + 'status_humi': None, + 'status_lux': '', + 'status_open': list([ + ]), + 'status_power': None, + 'status_saturation': None, + 'status_switch': None, + 'status_temp': 21, + 'status_temp_format': 'C', + 'status_total_energy': None, + 'thermo_c_setpoint': None, + 'thermo_c_setpoint_away': None, + 'thermo_fan_mode': None, + 'thermo_mode': None, + 'thermo_schd_setting': None, + 'thermo_setpoint': None, + 'thermo_setpoint_away': None, + 'trigger_by_zone': list([ + ]), + 'type': 'device_type.temperature_sensor', + 'type_no': '40', + }), ]), 'model': list([ dict({ @@ -1130,6 +1189,65 @@ 'type': 'device_type.door_lock', 'type_no': '72', }), + dict({ + 'address': '**REDACTED**', + 'area': '1', + 'bypass': '0', + 'device_group': '001', + 'device_id': '**REDACTED**', + 'device_id2': '', + 'extension': None, + 'group_id': None, + 'group_name': None, + 'ipcam_trigger_by_zone1': None, + 'ipcam_trigger_by_zone2': None, + 'ipcam_trigger_by_zone3': None, + 'ipcam_trigger_by_zone4': None, + 'mac': '**REDACTED**', + 'minigw_configuration_data': '', + 'minigw_lock_status': '', + 'minigw_number_of_credentials_supported': '', + 'minigw_product_data': '', + 'minigw_protocol': '', + 'minigw_syncing': '', + 'name': '**REDACTED**', + 'no': '8', + 'rf': None, + 'rssi': '9', + 'scene_restore': None, + 'scene_trigger': '0', + 'sresp_button_1': None, + 'sresp_button_2': None, + 'sresp_button_3': None, + 'sresp_button_4': None, + 'status1': '', + 'status2': None, + 'status_dim_level': None, + 'status_fault': list([ + ]), + 'status_hue': None, + 'status_humi': None, + 'status_lux': '', + 'status_open': list([ + ]), + 'status_power': None, + 'status_saturation': None, + 'status_switch': None, + 'status_temp': 21, + 'status_temp_format': 'C', + 'status_total_energy': None, + 'thermo_c_setpoint': None, + 'thermo_c_setpoint_away': None, + 'thermo_fan_mode': None, + 'thermo_mode': None, + 'thermo_schd_setting': None, + 'thermo_setpoint': None, + 'thermo_setpoint_away': None, + 'trigger_by_zone': list([ + ]), + 'type': 'device_type.temperature_sensor', + 'type_no': '40', + }), ]), 'HISTORY': list([ dict({ diff --git a/tests/components/yale_smart_alarm/test_sensor.py b/tests/components/yale_smart_alarm/test_sensor.py new file mode 100644 index 00000000000..d91ddc0e6ce --- /dev/null +++ b/tests/components/yale_smart_alarm/test_sensor.py @@ -0,0 +1,21 @@ +"""The test for the sensibo sensor.""" + +from __future__ import annotations + +from typing import Any +from unittest.mock import Mock + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def test_coordinator_setup_and_update_errors( + hass: HomeAssistant, + load_config_entry: tuple[MockConfigEntry, Mock], + load_json: dict[str, Any], +) -> None: + """Test the Yale Smart Living coordinator with errors.""" + + state = hass.states.get("sensor.smoke_alarm_temperature") + assert state.state == "21"