From 33d1cdd0ac4f00e5d88fa2afa3315d9ae4e45d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Farkasdi?= <93778865+farkasdi@users.noreply.github.com> Date: Thu, 8 Jan 2026 13:24:05 +0100 Subject: [PATCH] Refactor netatmo binary sensors (#160352) --- .../components/netatmo/binary_sensor.py | 91 +++++++++++++++---- homeassistant/components/netatmo/const.py | 1 + .../components/netatmo/data_handler.py | 45 +++++---- 3 files changed, 103 insertions(+), 34 deletions(-) diff --git a/homeassistant/components/netatmo/binary_sensor.py b/homeassistant/components/netatmo/binary_sensor.py index d35bfa7e8a6..81498c3d767 100644 --- a/homeassistant/components/netatmo/binary_sensor.py +++ b/homeassistant/components/netatmo/binary_sensor.py @@ -1,5 +1,9 @@ """Support for Netatmo binary sensors.""" +from dataclasses import dataclass +import logging +from typing import Final, cast + from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, @@ -9,17 +13,33 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback +from homeassistant.helpers.typing import StateType -from .const import NETATMO_CREATE_WEATHER_SENSOR +from .const import NETATMO_CREATE_WEATHER_BINARY_SENSOR from .data_handler import NetatmoDevice from .entity import NetatmoWeatherModuleEntity -BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = ( - BinarySensorEntityDescription( +_LOGGER = logging.getLogger(__name__) + + +@dataclass(frozen=True, kw_only=True) +class NetatmoBinarySensorEntityDescription(BinarySensorEntityDescription): + """Describes Netatmo binary sensor entity.""" + + name: str | None = None # The default name of the sensor + netatmo_name: str # The name used by Netatmo API for this sensor + + +NETATMO_WEATHER_BINARY_SENSOR_DESCRIPTIONS: Final[ + list[NetatmoBinarySensorEntityDescription] +] = [ + NetatmoBinarySensorEntityDescription( key="reachable", + name="Connectivity", + netatmo_name="reachable", device_class=BinarySensorDeviceClass.CONNECTIVITY, ), -) +] async def async_setup_entry( @@ -27,36 +47,75 @@ async def async_setup_entry( entry: ConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: - """Set up Netatmo binary sensors based on a config entry.""" + """Set up Netatmo weather binary sensors based on a config entry.""" @callback def _create_weather_binary_sensor_entity(netatmo_device: NetatmoDevice) -> None: - async_add_entities( - NetatmoWeatherBinarySensor(netatmo_device, description) - for description in BINARY_SENSOR_TYPES - if description.key in netatmo_device.device.features - ) + """Create weather binary sensor entities for a Netatmo weather device.""" + + descriptions_to_add = NETATMO_WEATHER_BINARY_SENSOR_DESCRIPTIONS + + entities: list[NetatmoWeatherBinarySensor] = [] + + # Create binary sensors for module + for description in descriptions_to_add: + # Actual check is simple for reachable + feature_check = description.key + if feature_check in netatmo_device.device.features: + _LOGGER.debug( + 'Adding "%s" weather binary sensor for device %s', + feature_check, + netatmo_device.device.name, + ) + entities.append( + NetatmoWeatherBinarySensor( + netatmo_device, + description, + ) + ) + + if entities: + async_add_entities(entities) entry.async_on_unload( async_dispatcher_connect( - hass, NETATMO_CREATE_WEATHER_SENSOR, _create_weather_binary_sensor_entity + hass, + NETATMO_CREATE_WEATHER_BINARY_SENSOR, + _create_weather_binary_sensor_entity, ) ) class NetatmoWeatherBinarySensor(NetatmoWeatherModuleEntity, BinarySensorEntity): - """Implementation of a Netatmo binary sensor.""" + """Implementation of a Netatmo weather binary sensor.""" + + entity_description: NetatmoBinarySensorEntityDescription def __init__( - self, device: NetatmoDevice, description: BinarySensorEntityDescription + self, + netatmo_device: NetatmoDevice, + description: NetatmoBinarySensorEntityDescription, ) -> None: - """Initialize a Netatmo binary sensor.""" - super().__init__(device) + """Initialize a Netatmo weather binary sensor.""" + + super().__init__(netatmo_device) + self.entity_description = description self._attr_unique_id = f"{self.device.entity_id}-{description.key}" @callback def async_update_callback(self) -> None: """Update the entity's state.""" - self._attr_is_on = self.device.reachable + + value: StateType | None = None + + value = getattr(self.device, self.entity_description.netatmo_name, None) + + if value is None: + self._attr_available = False + self._attr_is_on = False + else: + self._attr_available = True + self._attr_is_on = cast(bool, value) + self.async_write_ha_state() diff --git a/homeassistant/components/netatmo/const.py b/homeassistant/components/netatmo/const.py index 4207ec4eef1..e789885f56b 100644 --- a/homeassistant/components/netatmo/const.py +++ b/homeassistant/components/netatmo/const.py @@ -53,6 +53,7 @@ NETATMO_CREATE_ROOM_SENSOR = "netatmo_create_room_sensor" NETATMO_CREATE_SELECT = "netatmo_create_select" NETATMO_CREATE_SENSOR = "netatmo_create_sensor" NETATMO_CREATE_SWITCH = "netatmo_create_switch" +NETATMO_CREATE_WEATHER_BINARY_SENSOR = "netatmo_create_weather_binary_sensor" NETATMO_CREATE_WEATHER_SENSOR = "netatmo_create_weather_sensor" CONF_AREA_NAME = "area_name" diff --git a/homeassistant/components/netatmo/data_handler.py b/homeassistant/components/netatmo/data_handler.py index f6479d391fb..ee1b369c58c 100644 --- a/homeassistant/components/netatmo/data_handler.py +++ b/homeassistant/components/netatmo/data_handler.py @@ -45,6 +45,7 @@ from .const import ( NETATMO_CREATE_SELECT, NETATMO_CREATE_SENSOR, NETATMO_CREATE_SWITCH, + NETATMO_CREATE_WEATHER_BINARY_SENSOR, NETATMO_CREATE_WEATHER_SENSOR, PLATFORMS, WEBHOOK_ACTIVATION, @@ -332,16 +333,20 @@ class NetatmoDataHandler: """Set up home coach/air care modules.""" for module in self.account.modules.values(): if module.device_category is NetatmoDeviceCategory.air_care: - async_dispatcher_send( - self.hass, + for signal in ( + NETATMO_CREATE_WEATHER_BINARY_SENSOR, NETATMO_CREATE_WEATHER_SENSOR, - NetatmoDevice( - self, - module, - AIR_CARE, - AIR_CARE, - ), - ) + ): + async_dispatcher_send( + self.hass, + signal, + NetatmoDevice( + self, + module, + AIR_CARE, + AIR_CARE, + ), + ) def setup_modules(self, home: pyatmo.Home, signal_home: str) -> None: """Set up modules.""" @@ -379,16 +384,20 @@ class NetatmoDataHandler: ), ) if module.device_category is NetatmoDeviceCategory.weather: - async_dispatcher_send( - self.hass, + for signal in ( + NETATMO_CREATE_WEATHER_BINARY_SENSOR, NETATMO_CREATE_WEATHER_SENSOR, - NetatmoDevice( - self, - module, - home.entity_id, - WEATHER, - ), - ) + ): + async_dispatcher_send( + self.hass, + signal, + NetatmoDevice( + self, + module, + home.entity_id, + WEATHER, + ), + ) def setup_rooms(self, home: pyatmo.Home, signal_home: str) -> None: """Set up rooms."""