mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Add more base entities to netatmo (#107862)
* Rename netatmo base entity file * Add more Netatmo base entities * Add more Netatmo base entities * Add more Netatmo base entities * Add more Netatmo base entities * Apply suggestions from code review * Add more Netatmo base entities * Add snapshot tests to Netatmo platforms * Add snapshot tests to Netatmo platforms * Fix snapshots * Fix tests * Update snapshots * Add fans * Add fans * Update homeassistant/components/netatmo/select.py Co-authored-by: Tobias Sauerwein <cgtobi@users.noreply.github.com> * Add snapshot tests to Netatmo platforms * Update snapshots * minor clean up * Fix tests * Fix * Fix * Fix * Move dot split to weather station sensors --------- Co-authored-by: Tobias Sauerwein <cgtobi@users.noreply.github.com> Co-authored-by: Tobias Sauerwein <cgtobi@gmail.com>
This commit is contained in:
parent
9f2fa7ec19
commit
021eed66f3
@ -40,7 +40,7 @@ from .const import (
|
||||
WEBHOOK_PUSH_TYPE,
|
||||
)
|
||||
from .data_handler import EVENT, HOME, SIGNAL_NAME, NetatmoDevice
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoModuleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -80,12 +80,16 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
class NetatmoCamera(NetatmoModuleEntity, Camera):
|
||||
"""Representation of a Netatmo camera."""
|
||||
|
||||
_attr_brand = MANUFACTURER
|
||||
_attr_has_entity_name = True
|
||||
_attr_supported_features = CameraEntityFeature.STREAM
|
||||
_attr_configuration_url = CONF_URL_SECURITY
|
||||
device: NaModules.Camera
|
||||
_quality = DEFAULT_QUALITY
|
||||
_monitoring: bool | None = None
|
||||
_attr_name = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -93,30 +97,22 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
) -> None:
|
||||
"""Set up for access to the Netatmo camera images."""
|
||||
Camera.__init__(self)
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
|
||||
self._camera = cast(NaModules.Camera, netatmo_device.device)
|
||||
self._id = self._camera.entity_id
|
||||
self._home_id = self._camera.home.entity_id
|
||||
self._device_name = self._camera.name
|
||||
self._model = self._camera.device_type
|
||||
self._config_url = CONF_URL_SECURITY
|
||||
self._attr_unique_id = f"{self._id}-{self._model}"
|
||||
self._quality = DEFAULT_QUALITY
|
||||
self._monitoring: bool | None = None
|
||||
self._attr_unique_id = f"{netatmo_device.device.entity_id}-{self.device_type}"
|
||||
self._light_state = None
|
||||
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._home_id,
|
||||
SIGNAL_NAME: f"{HOME}-{self._home_id}",
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: f"{HOME}-{self.home.entity_id}",
|
||||
},
|
||||
{
|
||||
"name": EVENT,
|
||||
"home_id": self._home_id,
|
||||
SIGNAL_NAME: f"{EVENT}-{self._home_id}",
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: f"{EVENT}-{self.home.entity_id}",
|
||||
},
|
||||
]
|
||||
)
|
||||
@ -134,7 +130,7 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
)
|
||||
)
|
||||
|
||||
self.hass.data[DOMAIN][DATA_CAMERAS][self._id] = self._device_name
|
||||
self.hass.data[DOMAIN][DATA_CAMERAS][self.device.entity_id] = self.device.name
|
||||
|
||||
@callback
|
||||
def handle_event(self, event: dict) -> None:
|
||||
@ -144,7 +140,10 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
if not data.get("camera_id"):
|
||||
return
|
||||
|
||||
if data["home_id"] == self._home_id and data["camera_id"] == self._id:
|
||||
if (
|
||||
data["home_id"] == self.home.entity_id
|
||||
and data["camera_id"] == self.device.entity_id
|
||||
):
|
||||
if data[WEBHOOK_PUSH_TYPE] in ("NACamera-off", "NACamera-disconnection"):
|
||||
self._attr_is_streaming = False
|
||||
self._monitoring = False
|
||||
@ -168,7 +167,7 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
) -> bytes | None:
|
||||
"""Return a still image response from the camera."""
|
||||
try:
|
||||
return cast(bytes, await self._camera.async_get_live_snapshot())
|
||||
return cast(bytes, await self.device.async_get_live_snapshot())
|
||||
except (
|
||||
aiohttp.ClientPayloadError,
|
||||
aiohttp.ContentTypeError,
|
||||
@ -183,50 +182,50 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
def supported_features(self) -> CameraEntityFeature:
|
||||
"""Return supported features."""
|
||||
supported_features = CameraEntityFeature.ON_OFF
|
||||
if self._model != "NDB":
|
||||
if self.device_type != "NDB":
|
||||
supported_features |= CameraEntityFeature.STREAM
|
||||
return supported_features
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn off camera."""
|
||||
await self._camera.async_monitoring_off()
|
||||
await self.device.async_monitoring_off()
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn on camera."""
|
||||
await self._camera.async_monitoring_on()
|
||||
await self.device.async_monitoring_on()
|
||||
|
||||
async def stream_source(self) -> str:
|
||||
"""Return the stream source."""
|
||||
if self._camera.is_local:
|
||||
await self._camera.async_update_camera_urls()
|
||||
if self.device.is_local:
|
||||
await self.device.async_update_camera_urls()
|
||||
|
||||
if self._camera.local_url:
|
||||
return f"{self._camera.local_url}/live/files/{self._quality}/index.m3u8"
|
||||
return f"{self._camera.vpn_url}/live/files/{self._quality}/index.m3u8"
|
||||
if self.device.local_url:
|
||||
return f"{self.device.local_url}/live/files/{self._quality}/index.m3u8"
|
||||
return f"{self.device.vpn_url}/live/files/{self._quality}/index.m3u8"
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._attr_is_on = self._camera.alim_status is not None
|
||||
self._attr_available = self._camera.alim_status is not None
|
||||
self._attr_is_on = self.device.alim_status is not None
|
||||
self._attr_available = self.device.alim_status is not None
|
||||
|
||||
if self._camera.monitoring is not None:
|
||||
self._attr_is_streaming = self._camera.monitoring
|
||||
self._attr_motion_detection_enabled = self._camera.monitoring
|
||||
if self.device.monitoring is not None:
|
||||
self._attr_is_streaming = self.device.monitoring
|
||||
self._attr_motion_detection_enabled = self.device.monitoring
|
||||
|
||||
self.hass.data[DOMAIN][DATA_EVENTS][self._id] = self.process_events(
|
||||
self._camera.events
|
||||
self.hass.data[DOMAIN][DATA_EVENTS][self.device.entity_id] = (
|
||||
self.process_events(self.device.events)
|
||||
)
|
||||
|
||||
self._attr_extra_state_attributes.update(
|
||||
{
|
||||
"id": self._id,
|
||||
"id": self.device.entity_id,
|
||||
"monitoring": self._monitoring,
|
||||
"sd_status": self._camera.sd_status,
|
||||
"alim_status": self._camera.alim_status,
|
||||
"is_local": self._camera.is_local,
|
||||
"vpn_url": self._camera.vpn_url,
|
||||
"local_url": self._camera.local_url,
|
||||
"sd_status": self.device.sd_status,
|
||||
"alim_status": self.device.alim_status,
|
||||
"is_local": self.device.is_local,
|
||||
"vpn_url": self.device.vpn_url,
|
||||
"local_url": self.device.local_url,
|
||||
"light_state": self._light_state,
|
||||
}
|
||||
)
|
||||
@ -249,9 +248,9 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
|
||||
def get_video_url(self, video_id: str) -> str:
|
||||
"""Get video url."""
|
||||
if self._camera.is_local:
|
||||
return f"{self._camera.local_url}/vod/{video_id}/files/{self._quality}/index.m3u8"
|
||||
return f"{self._camera.vpn_url}/vod/{video_id}/files/{self._quality}/index.m3u8"
|
||||
if self.device.is_local:
|
||||
return f"{self.device.local_url}/vod/{video_id}/files/{self._quality}/index.m3u8"
|
||||
return f"{self.device.vpn_url}/vod/{video_id}/files/{self._quality}/index.m3u8"
|
||||
|
||||
def fetch_person_ids(self, persons: list[str | None]) -> list[str]:
|
||||
"""Fetch matching person ids for given list of persons."""
|
||||
@ -260,7 +259,7 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
|
||||
for person in persons:
|
||||
person_id = None
|
||||
for pid, data in self._camera.home.persons.items():
|
||||
for pid, data in self.home.persons.items():
|
||||
if data.pseudo == person:
|
||||
person_ids.append(pid)
|
||||
person_id = pid
|
||||
@ -279,7 +278,7 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
persons = kwargs.get(ATTR_PERSONS, [])
|
||||
person_ids = self.fetch_person_ids(persons)
|
||||
|
||||
await self._camera.home.async_set_persons_home(person_ids=person_ids)
|
||||
await self.home.async_set_persons_home(person_ids=person_ids)
|
||||
_LOGGER.debug("Set %s as at home", persons)
|
||||
|
||||
async def _service_set_person_away(self, **kwargs: Any) -> None:
|
||||
@ -288,7 +287,7 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
person_ids = self.fetch_person_ids([person] if person else [])
|
||||
person_id = next(iter(person_ids), None)
|
||||
|
||||
await self._camera.home.async_set_persons_away(
|
||||
await self.home.async_set_persons_away(
|
||||
person_id=person_id,
|
||||
)
|
||||
|
||||
@ -299,11 +298,11 @@ class NetatmoCamera(NetatmoBaseEntity, Camera):
|
||||
|
||||
async def _service_set_camera_light(self, **kwargs: Any) -> None:
|
||||
"""Service to set light mode."""
|
||||
if not isinstance(self._camera, NaModules.netatmo.NOC):
|
||||
if not isinstance(self.device, NaModules.netatmo.NOC):
|
||||
raise HomeAssistantError(
|
||||
f"{self._model} <{self._device_name}> does not have a floodlight"
|
||||
f"{self.device_type} <{self.device.name}> does not have a floodlight"
|
||||
)
|
||||
|
||||
mode = str(kwargs.get(ATTR_CAMERA_LIGHT_MODE))
|
||||
_LOGGER.debug("Turn %s camera light for '%s'", mode, self._attr_name)
|
||||
await self._camera.async_set_floodlight_state(mode)
|
||||
await self.device.async_set_floodlight_state(mode)
|
||||
|
@ -22,7 +22,6 @@ from homeassistant.components.climate import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_SUGGESTED_AREA,
|
||||
ATTR_TEMPERATURE,
|
||||
PRECISION_HALVES,
|
||||
STATE_OFF,
|
||||
@ -30,7 +29,6 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
@ -42,7 +40,6 @@ from .const import (
|
||||
ATTR_SELECTED_SCHEDULE,
|
||||
ATTR_TARGET_TEMPERATURE,
|
||||
ATTR_TIME_PERIOD,
|
||||
CONF_URL_ENERGY,
|
||||
DATA_SCHEDULES,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_CANCEL_SET_POINT,
|
||||
@ -57,7 +54,7 @@ from .const import (
|
||||
SERVICE_SET_TEMPERATURE_WITH_TIME_PERIOD,
|
||||
)
|
||||
from .data_handler import HOME, SIGNAL_NAME, NetatmoRoom
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoRoomEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -182,7 +179,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
class NetatmoThermostat(NetatmoRoomEntity, ClimateEntity):
|
||||
"""Representation a Netatmo thermostat."""
|
||||
|
||||
_attr_hvac_mode = HVACMode.AUTO
|
||||
@ -191,47 +188,37 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
_attr_supported_features = SUPPORT_FLAGS
|
||||
_attr_target_temperature_step = PRECISION_HALVES
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_name = None
|
||||
_away: bool | None = None
|
||||
_connected: bool | None = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
|
||||
def __init__(self, netatmo_device: NetatmoRoom) -> None:
|
||||
_away_temperature: float | None = None
|
||||
_hg_temperature: float | None = None
|
||||
_boilerstatus: bool | None = None
|
||||
|
||||
def __init__(self, room: NetatmoRoom) -> None:
|
||||
"""Initialize the sensor."""
|
||||
ClimateEntity.__init__(self)
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(room)
|
||||
|
||||
self._room = netatmo_device.room
|
||||
self._id = self._room.entity_id
|
||||
self._home_id = self._room.home.entity_id
|
||||
|
||||
self._signal_name = f"{HOME}-{self._home_id}"
|
||||
self._signal_name = f"{HOME}-{self.home.entity_id}"
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._room.home.entity_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
assert self._room.climate_type
|
||||
self._model: DeviceType = self._room.climate_type
|
||||
|
||||
self._config_url = CONF_URL_ENERGY
|
||||
|
||||
self._attr_name = self._room.name
|
||||
self._away: bool | None = None
|
||||
self._connected: bool | None = None
|
||||
|
||||
self._away_temperature: float | None = None
|
||||
self._hg_temperature: float | None = None
|
||||
self._boilerstatus: bool | None = None
|
||||
self._selected_schedule = None
|
||||
|
||||
self._attr_hvac_modes = [HVACMode.AUTO, HVACMode.HEAT]
|
||||
if self._model is NA_THERM:
|
||||
if self.device_type is NA_THERM:
|
||||
self._attr_hvac_modes.append(HVACMode.OFF)
|
||||
|
||||
self._attr_unique_id = f"{self._room.entity_id}-{self._model}"
|
||||
self._attr_unique_id = f"{self.device.entity_id}-{self.device_type}"
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Entity created."""
|
||||
@ -256,12 +243,12 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
"""Handle webhook events."""
|
||||
data = event["data"]
|
||||
|
||||
if self._room.home.entity_id != data["home_id"]:
|
||||
if self.home.entity_id != data["home_id"]:
|
||||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_SCHEDULE and "schedule_id" in data:
|
||||
self._selected_schedule = getattr(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._room.home.entity_id].get(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self.home.entity_id].get(
|
||||
data["schedule_id"]
|
||||
),
|
||||
"name",
|
||||
@ -276,7 +263,7 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
|
||||
home = data["home"]
|
||||
|
||||
if self._room.home.entity_id != home["id"]:
|
||||
if self.home.entity_id != home["id"]:
|
||||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_THERM_MODE:
|
||||
@ -295,7 +282,7 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
for room in home.get("rooms", []):
|
||||
if (
|
||||
data["event_type"] == EVENT_TYPE_SET_POINT
|
||||
and self._room.entity_id == room["id"]
|
||||
and self.device.entity_id == room["id"]
|
||||
):
|
||||
if room["therm_setpoint_mode"] == STATE_NETATMO_OFF:
|
||||
self._attr_hvac_mode = HVACMode.OFF
|
||||
@ -317,7 +304,7 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
|
||||
if (
|
||||
data["event_type"] == EVENT_TYPE_CANCEL_SET_POINT
|
||||
and self._room.entity_id == room["id"]
|
||||
and self.device.entity_id == room["id"]
|
||||
):
|
||||
if self._attr_hvac_mode == HVACMode.OFF:
|
||||
self._attr_hvac_mode = HVACMode.AUTO
|
||||
@ -330,11 +317,11 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
@property
|
||||
def hvac_action(self) -> HVACAction:
|
||||
"""Return the current running hvac operation if supported."""
|
||||
if self._model != NA_VALVE and self._boilerstatus is not None:
|
||||
if self.device_type != NA_VALVE and self._boilerstatus is not None:
|
||||
return CURRENT_HVAC_MAP_NETATMO[self._boilerstatus]
|
||||
# Maybe it is a valve
|
||||
if (
|
||||
heating_req := getattr(self._room, "heating_power_request", 0)
|
||||
heating_req := getattr(self.device, "heating_power_request", 0)
|
||||
) is not None and heating_req > 0:
|
||||
return HVACAction.HEATING
|
||||
return HVACAction.IDLE
|
||||
@ -352,16 +339,17 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
"""Set new preset mode."""
|
||||
if (
|
||||
preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX)
|
||||
and self._model == NA_VALVE
|
||||
and self.device_type == NA_VALVE
|
||||
and self._attr_hvac_mode == HVACMode.HEAT
|
||||
):
|
||||
await self._room.async_therm_set(
|
||||
await self.device.async_therm_set(
|
||||
STATE_NETATMO_HOME,
|
||||
)
|
||||
elif (
|
||||
preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX) and self._model == NA_VALVE
|
||||
preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX)
|
||||
and self.device_type == NA_VALVE
|
||||
):
|
||||
await self._room.async_therm_set(
|
||||
await self.device.async_therm_set(
|
||||
STATE_NETATMO_MANUAL,
|
||||
DEFAULT_MAX_TEMP,
|
||||
)
|
||||
@ -369,11 +357,11 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX)
|
||||
and self._attr_hvac_mode == HVACMode.HEAT
|
||||
):
|
||||
await self._room.async_therm_set(STATE_NETATMO_HOME)
|
||||
await self.device.async_therm_set(STATE_NETATMO_HOME)
|
||||
elif preset_mode in (PRESET_BOOST, STATE_NETATMO_MAX):
|
||||
await self._room.async_therm_set(PRESET_MAP_NETATMO[preset_mode])
|
||||
await self.device.async_therm_set(PRESET_MAP_NETATMO[preset_mode])
|
||||
elif preset_mode in THERM_MODES:
|
||||
await self._room.home.async_set_thermmode(PRESET_MAP_NETATMO[preset_mode])
|
||||
await self.device.home.async_set_thermmode(PRESET_MAP_NETATMO[preset_mode])
|
||||
else:
|
||||
_LOGGER.error("Preset mode '%s' not available", preset_mode)
|
||||
|
||||
@ -381,25 +369,25 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature for 2 hours."""
|
||||
await self._room.async_therm_set(
|
||||
await self.device.async_therm_set(
|
||||
STATE_NETATMO_MANUAL, min(kwargs[ATTR_TEMPERATURE], DEFAULT_MAX_TEMP)
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
"""Turn the entity off."""
|
||||
if self._model == NA_VALVE:
|
||||
await self._room.async_therm_set(
|
||||
if self.device_type == NA_VALVE:
|
||||
await self.device.async_therm_set(
|
||||
STATE_NETATMO_MANUAL,
|
||||
DEFAULT_MIN_TEMP,
|
||||
)
|
||||
elif self._attr_hvac_mode != HVACMode.OFF:
|
||||
await self._room.async_therm_set(STATE_NETATMO_OFF)
|
||||
await self.device.async_therm_set(STATE_NETATMO_OFF)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the entity on."""
|
||||
await self._room.async_therm_set(STATE_NETATMO_HOME)
|
||||
await self.device.async_therm_set(STATE_NETATMO_HOME)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
@ -410,36 +398,36 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
if not self._room.reachable:
|
||||
if not self.device.reachable:
|
||||
if self.available:
|
||||
self._connected = False
|
||||
return
|
||||
|
||||
self._connected = True
|
||||
|
||||
self._away_temperature = self._room.home.get_away_temp()
|
||||
self._hg_temperature = self._room.home.get_hg_temp()
|
||||
self._attr_current_temperature = self._room.therm_measured_temperature
|
||||
self._attr_target_temperature = self._room.therm_setpoint_temperature
|
||||
self._away_temperature = self.home.get_away_temp()
|
||||
self._hg_temperature = self.home.get_hg_temp()
|
||||
self._attr_current_temperature = self.device.therm_measured_temperature
|
||||
self._attr_target_temperature = self.device.therm_setpoint_temperature
|
||||
self._attr_preset_mode = NETATMO_MAP_PRESET[
|
||||
getattr(self._room, "therm_setpoint_mode", STATE_NETATMO_SCHEDULE)
|
||||
getattr(self.device, "therm_setpoint_mode", STATE_NETATMO_SCHEDULE)
|
||||
]
|
||||
self._attr_hvac_mode = HVAC_MAP_NETATMO[self._attr_preset_mode]
|
||||
self._away = self._attr_hvac_mode == HVAC_MAP_NETATMO[STATE_NETATMO_AWAY]
|
||||
|
||||
self._selected_schedule = getattr(
|
||||
self._room.home.get_selected_schedule(), "name", None
|
||||
self.home.get_selected_schedule(), "name", None
|
||||
)
|
||||
self._attr_extra_state_attributes[ATTR_SELECTED_SCHEDULE] = (
|
||||
self._selected_schedule
|
||||
)
|
||||
|
||||
if self._model == NA_VALVE:
|
||||
if self.device_type == NA_VALVE:
|
||||
self._attr_extra_state_attributes[ATTR_HEATING_POWER_REQUEST] = (
|
||||
self._room.heating_power_request
|
||||
self.device.heating_power_request
|
||||
)
|
||||
else:
|
||||
for module in self._room.modules.values():
|
||||
for module in self.device.modules.values():
|
||||
if hasattr(module, "boiler_status"):
|
||||
module = cast(NATherm1, module)
|
||||
if module.boiler_status is not None:
|
||||
@ -450,7 +438,7 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
schedule_name = kwargs.get(ATTR_SCHEDULE_NAME)
|
||||
schedule_id = None
|
||||
for sid, schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._room.home.entity_id
|
||||
self.home.entity_id
|
||||
].items():
|
||||
if schedule.name == schedule_name:
|
||||
schedule_id = sid
|
||||
@ -460,10 +448,10 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
_LOGGER.error("%s is not a valid schedule", kwargs.get(ATTR_SCHEDULE_NAME))
|
||||
return
|
||||
|
||||
await self._room.home.async_switch_schedule(schedule_id=schedule_id)
|
||||
await self.home.async_switch_schedule(schedule_id=schedule_id)
|
||||
_LOGGER.debug(
|
||||
"Setting %s schedule to %s (%s)",
|
||||
self._room.home.entity_id,
|
||||
self.home.entity_id,
|
||||
kwargs.get(ATTR_SCHEDULE_NAME),
|
||||
schedule_id,
|
||||
)
|
||||
@ -475,12 +463,12 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
end_datetime = kwargs[ATTR_END_DATETIME]
|
||||
end_timestamp = int(dt_util.as_timestamp(end_datetime))
|
||||
|
||||
await self._room.home.async_set_thermmode(
|
||||
await self.home.async_set_thermmode(
|
||||
mode=PRESET_MAP_NETATMO[preset_mode], end_time=end_timestamp
|
||||
)
|
||||
_LOGGER.debug(
|
||||
"Setting %s preset to %s with end datetime %s",
|
||||
self._room.home.entity_id,
|
||||
self.home.entity_id,
|
||||
preset_mode,
|
||||
end_timestamp,
|
||||
)
|
||||
@ -494,11 +482,11 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
|
||||
_LOGGER.debug(
|
||||
"Setting %s to target temperature %s with end datetime %s",
|
||||
self._room.entity_id,
|
||||
self.device.entity_id,
|
||||
target_temperature,
|
||||
end_timestamp,
|
||||
)
|
||||
await self._room.async_therm_manual(target_temperature, end_timestamp)
|
||||
await self.device.async_therm_manual(target_temperature, end_timestamp)
|
||||
|
||||
async def _async_service_set_temperature_with_time_period(
|
||||
self, **kwargs: Any
|
||||
@ -508,22 +496,15 @@ class NetatmoThermostat(NetatmoBaseEntity, ClimateEntity):
|
||||
|
||||
_LOGGER.debug(
|
||||
"Setting %s to target temperature %s with time period %s",
|
||||
self._room.entity_id,
|
||||
self.device.entity_id,
|
||||
target_temperature,
|
||||
time_period,
|
||||
)
|
||||
|
||||
now_timestamp = dt_util.as_timestamp(dt_util.utcnow())
|
||||
end_timestamp = int(now_timestamp + time_period.seconds)
|
||||
await self._room.async_therm_manual(target_temperature, end_timestamp)
|
||||
await self.device.async_therm_manual(target_temperature, end_timestamp)
|
||||
|
||||
async def _async_service_clear_temperature_setting(self, **kwargs: Any) -> None:
|
||||
_LOGGER.debug("Clearing %s temperature setting", self._room.entity_id)
|
||||
await self._room.async_therm_home()
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device info for the thermostat."""
|
||||
device_info: DeviceInfo = super().device_info
|
||||
device_info[ATTR_SUGGESTED_AREA] = self._room.name
|
||||
return device_info
|
||||
_LOGGER.debug("Clearing %s temperature setting", self.device.entity_id)
|
||||
await self.device.async_therm_home()
|
||||
|
@ -12,6 +12,7 @@ PLATFORMS = [
|
||||
Platform.CAMERA,
|
||||
Platform.CLIMATE,
|
||||
Platform.COVER,
|
||||
Platform.FAN,
|
||||
Platform.LIGHT,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
|
@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
from pyatmo import modules as NaModules
|
||||
|
||||
@ -20,7 +20,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import CONF_URL_CONTROL, NETATMO_CREATE_COVER
|
||||
from .data_handler import HOME, SIGNAL_NAME, NetatmoDevice
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoModuleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -43,7 +43,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class NetatmoCover(NetatmoBaseEntity, CoverEntity):
|
||||
class NetatmoCover(NetatmoModuleEntity, CoverEntity):
|
||||
"""Representation of a Netatmo cover device."""
|
||||
|
||||
_attr_supported_features = (
|
||||
@ -52,56 +52,51 @@ class NetatmoCover(NetatmoBaseEntity, CoverEntity):
|
||||
| CoverEntityFeature.STOP
|
||||
| CoverEntityFeature.SET_POSITION
|
||||
)
|
||||
_attr_configuration_url = CONF_URL_CONTROL
|
||||
_attr_device_class = CoverDeviceClass.SHUTTER
|
||||
_attr_name = None
|
||||
device: NaModules.Shutter
|
||||
|
||||
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
||||
"""Initialize the Netatmo device."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
|
||||
self._cover = cast(NaModules.Shutter, netatmo_device.device)
|
||||
self._attr_is_closed = self.device.current_position == 0
|
||||
|
||||
self._id = self._cover.entity_id
|
||||
self._attr_name = self._device_name = self._cover.name
|
||||
self._model = self._cover.device_type
|
||||
self._config_url = CONF_URL_CONTROL
|
||||
|
||||
self._home_id = self._cover.home.entity_id
|
||||
self._attr_is_closed = self._cover.current_position == 0
|
||||
|
||||
self._signal_name = f"{HOME}-{self._home_id}"
|
||||
self._signal_name = f"{HOME}-{self.home.entity_id}"
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._home_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
self._attr_unique_id = f"{self._id}-{self._model}"
|
||||
self._attr_unique_id = f"{self.device.entity_id}-{self.device_type}"
|
||||
|
||||
async def async_close_cover(self, **kwargs: Any) -> None:
|
||||
"""Close the cover."""
|
||||
await self._cover.async_close()
|
||||
await self.device.async_close()
|
||||
self._attr_is_closed = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_open_cover(self, **kwargs: Any) -> None:
|
||||
"""Open the cover."""
|
||||
await self._cover.async_open()
|
||||
await self.device.async_open()
|
||||
self._attr_is_closed = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_stop_cover(self, **kwargs: Any) -> None:
|
||||
"""Stop the cover."""
|
||||
await self._cover.async_stop()
|
||||
await self.device.async_stop()
|
||||
|
||||
async def async_set_cover_position(self, **kwargs: Any) -> None:
|
||||
"""Move the cover shutter to a specific position."""
|
||||
await self._cover.async_set_target_position(kwargs[ATTR_POSITION])
|
||||
await self.device.async_set_target_position(kwargs[ATTR_POSITION])
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._attr_is_closed = self._cover.current_position == 0
|
||||
self._attr_current_cover_position = self._cover.current_position
|
||||
self._attr_is_closed = self.device.current_position == 0
|
||||
self._attr_current_cover_position = self.device.current_position
|
||||
|
@ -2,39 +2,38 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import Any
|
||||
|
||||
from pyatmo import DeviceType
|
||||
from pyatmo.modules.device_types import (
|
||||
DEVICE_DESCRIPTION_MAP,
|
||||
DeviceType as NetatmoDeviceType,
|
||||
)
|
||||
from pyatmo import DeviceType, Home, Module, Room
|
||||
from pyatmo.modules.base_class import NetatmoBase
|
||||
from pyatmo.modules.device_types import DEVICE_DESCRIPTION_MAP
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import DATA_DEVICE_IDS, DEFAULT_ATTRIBUTION, DOMAIN, SIGNAL_NAME
|
||||
from .data_handler import PUBLIC, NetatmoDataHandler
|
||||
from .const import (
|
||||
CONF_URL_ENERGY,
|
||||
DATA_DEVICE_IDS,
|
||||
DEFAULT_ATTRIBUTION,
|
||||
DOMAIN,
|
||||
SIGNAL_NAME,
|
||||
)
|
||||
from .data_handler import PUBLIC, NetatmoDataHandler, NetatmoDevice, NetatmoRoom
|
||||
|
||||
|
||||
class NetatmoBaseEntity(Entity):
|
||||
"""Netatmo entity base class."""
|
||||
|
||||
_attr_attribution = DEFAULT_ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, data_handler: NetatmoDataHandler) -> None:
|
||||
"""Set up Netatmo entity base."""
|
||||
self.data_handler = data_handler
|
||||
self._publishers: list[dict[str, Any]] = []
|
||||
|
||||
self._device_name: str = ""
|
||||
self._id: str = ""
|
||||
self._model: DeviceType
|
||||
self._config_url: str | None = None
|
||||
self._attr_name = None
|
||||
self._attr_unique_id = None
|
||||
self._attr_extra_state_attributes = {}
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
@ -72,10 +71,6 @@ class NetatmoBaseEntity(Entity):
|
||||
):
|
||||
await self.data_handler.unsubscribe(signal_name, None)
|
||||
|
||||
registry = dr.async_get(self.hass)
|
||||
if device := registry.async_get_device(identifiers={(DOMAIN, self._id)}):
|
||||
self.hass.data[DOMAIN][DATA_DEVICE_IDS][self._id] = device.id
|
||||
|
||||
self.async_update_callback()
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
@ -92,18 +87,82 @@ class NetatmoBaseEntity(Entity):
|
||||
"""Update the entity's state."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class NetatmoDeviceEntity(NetatmoBaseEntity):
|
||||
"""Netatmo entity base class."""
|
||||
|
||||
def __init__(self, data_handler: NetatmoDataHandler, device: NetatmoBase) -> None:
|
||||
"""Set up Netatmo entity base."""
|
||||
super().__init__(data_handler)
|
||||
self.device = device
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return the device info for the sensor."""
|
||||
if "." in self._model:
|
||||
netatmo_device = NetatmoDeviceType(self._model.partition(".")[2])
|
||||
else:
|
||||
netatmo_device = getattr(NetatmoDeviceType, self._model)
|
||||
manufacturer, model = DEVICE_DESCRIPTION_MAP[netatmo_device]
|
||||
return DeviceInfo(
|
||||
configuration_url=self._config_url,
|
||||
identifiers={(DOMAIN, self._id)},
|
||||
name=self._device_name,
|
||||
manufacturer=manufacturer,
|
||||
model=model,
|
||||
@abstractmethod
|
||||
def device_type(self) -> DeviceType:
|
||||
"""Return the device type."""
|
||||
|
||||
@property
|
||||
def device_description(self) -> tuple[str, str]:
|
||||
"""Return the model of this device."""
|
||||
return DEVICE_DESCRIPTION_MAP[self.device_type]
|
||||
|
||||
@property
|
||||
def home(self) -> Home:
|
||||
"""Return the home this room belongs to."""
|
||||
return self.device.home
|
||||
|
||||
|
||||
class NetatmoRoomEntity(NetatmoDeviceEntity):
|
||||
"""Netatmo room entity base class."""
|
||||
|
||||
device: Room
|
||||
|
||||
def __init__(self, room: NetatmoRoom) -> None:
|
||||
"""Set up a Netatmo room entity."""
|
||||
super().__init__(room.data_handler, room.room)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, room.room.entity_id)},
|
||||
name=room.room.name,
|
||||
manufacturer=self.device_description[0],
|
||||
model=self.device_description[1],
|
||||
configuration_url=CONF_URL_ENERGY,
|
||||
suggested_area=room.room.name,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Entity created."""
|
||||
await super().async_added_to_hass()
|
||||
registry = dr.async_get(self.hass)
|
||||
if device := registry.async_get_device(
|
||||
identifiers={(DOMAIN, self.device.entity_id)}
|
||||
):
|
||||
self.hass.data[DOMAIN][DATA_DEVICE_IDS][self.device.entity_id] = device.id
|
||||
|
||||
@property
|
||||
def device_type(self) -> DeviceType:
|
||||
"""Return the device type."""
|
||||
assert self.device.climate_type
|
||||
return self.device.climate_type
|
||||
|
||||
|
||||
class NetatmoModuleEntity(NetatmoDeviceEntity):
|
||||
"""Netatmo module entity base class."""
|
||||
|
||||
device: Module
|
||||
_attr_configuration_url: str
|
||||
|
||||
def __init__(self, device: NetatmoDevice) -> None:
|
||||
"""Set up a Netatmo module entity."""
|
||||
super().__init__(device.data_handler, device.device)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, device.device.entity_id)},
|
||||
name=device.device.name,
|
||||
manufacturer=self.device_description[0],
|
||||
model=self.device_description[1],
|
||||
configuration_url=self._attr_configuration_url,
|
||||
)
|
||||
|
||||
@property
|
||||
def device_type(self) -> DeviceType:
|
||||
"""Return the device type."""
|
||||
return self.device.device_type
|
||||
|
@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Final, cast
|
||||
from typing import Final
|
||||
|
||||
from pyatmo import modules as NaModules
|
||||
|
||||
@ -15,7 +15,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import CONF_URL_CONTROL, NETATMO_CREATE_FAN
|
||||
from .data_handler import HOME, SIGNAL_NAME, NetatmoDevice
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoModuleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -43,46 +43,38 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class NetatmoFan(NetatmoBaseEntity, FanEntity):
|
||||
class NetatmoFan(NetatmoModuleEntity, FanEntity):
|
||||
"""Representation of a Netatmo fan."""
|
||||
|
||||
_attr_preset_modes = ["slow", "fast"]
|
||||
_attr_supported_features = FanEntityFeature.PRESET_MODE
|
||||
_attr_configuration_url = CONF_URL_CONTROL
|
||||
_attr_name = None
|
||||
device: NaModules.Fan
|
||||
|
||||
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
||||
"""Initialize of Netatmo fan."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
|
||||
self._fan = cast(NaModules.Fan, netatmo_device.device)
|
||||
|
||||
self._id = self._fan.entity_id
|
||||
self._attr_name = self._device_name = self._fan.name
|
||||
self._model = self._fan.device_type
|
||||
self._config_url = CONF_URL_CONTROL
|
||||
|
||||
self._home_id = self._fan.home.entity_id
|
||||
|
||||
self._signal_name = f"{HOME}-{self._home_id}"
|
||||
super().__init__(netatmo_device)
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._home_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: f"{HOME}-{self.home.entity_id}",
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self._attr_unique_id = f"{self._id}-{self._model}"
|
||||
self._attr_unique_id = f"{self.device.entity_id}-{self.device_type}"
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set the preset mode of the fan."""
|
||||
await self._fan.async_set_fan_speed(PRESET_MAPPING[preset_mode])
|
||||
await self.device.async_set_fan_speed(PRESET_MAPPING[preset_mode])
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
if self._fan.fan_speed is None:
|
||||
if self.device.fan_speed is None:
|
||||
self._attr_preset_mode = None
|
||||
return
|
||||
self._attr_preset_mode = PRESETS.get(self._fan.fan_speed)
|
||||
self._attr_preset_mode = PRESETS.get(self.device.fan_speed)
|
||||
|
@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
from pyatmo import modules as NaModules
|
||||
|
||||
@ -24,7 +24,7 @@ from .const import (
|
||||
WEBHOOK_PUSH_TYPE,
|
||||
)
|
||||
from .data_handler import HOME, SIGNAL_NAME, NetatmoDevice
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoModuleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -62,36 +62,28 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class NetatmoCameraLight(NetatmoBaseEntity, LightEntity):
|
||||
class NetatmoCameraLight(NetatmoModuleEntity, LightEntity):
|
||||
"""Representation of a Netatmo Presence camera light."""
|
||||
|
||||
device: NaModules.NOC
|
||||
_attr_is_on = False
|
||||
_attr_name = None
|
||||
_attr_configuration_url = CONF_URL_SECURITY
|
||||
_attr_color_mode = ColorMode.ONOFF
|
||||
_attr_has_entity_name = True
|
||||
_attr_supported_color_modes = {ColorMode.ONOFF}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
netatmo_device: NetatmoDevice,
|
||||
) -> None:
|
||||
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
||||
"""Initialize a Netatmo Presence camera light."""
|
||||
LightEntity.__init__(self)
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
self._attr_unique_id = f"{self.device.entity_id}-light"
|
||||
|
||||
self._camera = cast(NaModules.NOC, netatmo_device.device)
|
||||
self._id = self._camera.entity_id
|
||||
self._home_id = self._camera.home.entity_id
|
||||
self._device_name = self._camera.name
|
||||
self._model = self._camera.device_type
|
||||
self._config_url = CONF_URL_SECURITY
|
||||
self._is_on = False
|
||||
self._attr_unique_id = f"{self._id}-light"
|
||||
|
||||
self._signal_name = f"{HOME}-{self._home_id}"
|
||||
self._signal_name = f"{HOME}-{self.home.entity_id}"
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._camera.home.entity_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
},
|
||||
]
|
||||
@ -118,11 +110,11 @@ class NetatmoCameraLight(NetatmoBaseEntity, LightEntity):
|
||||
return
|
||||
|
||||
if (
|
||||
data["home_id"] == self._home_id
|
||||
and data["camera_id"] == self._id
|
||||
data["home_id"] == self.home.entity_id
|
||||
and data["camera_id"] == self.device.entity_id
|
||||
and data[WEBHOOK_PUSH_TYPE] == WEBHOOK_LIGHT_MODE
|
||||
):
|
||||
self._is_on = bool(data["sub_type"] == "on")
|
||||
self._attr_is_on = bool(data["sub_type"] == "on")
|
||||
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
@ -132,59 +124,47 @@ class NetatmoCameraLight(NetatmoBaseEntity, LightEntity):
|
||||
"""If the webhook is not established, mark as unavailable."""
|
||||
return bool(self.data_handler.webhook)
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if light is on."""
|
||||
return self._is_on
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn camera floodlight on."""
|
||||
_LOGGER.debug("Turn camera '%s' on", self.name)
|
||||
await self._camera.async_floodlight_on()
|
||||
await self.device.async_floodlight_on()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn camera floodlight into auto mode."""
|
||||
_LOGGER.debug("Turn camera '%s' to auto mode", self.name)
|
||||
await self._camera.async_floodlight_auto()
|
||||
await self.device.async_floodlight_auto()
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._is_on = bool(self._camera.floodlight == "on")
|
||||
self._attr_is_on = bool(self.device.floodlight == "on")
|
||||
|
||||
|
||||
class NetatmoLight(NetatmoBaseEntity, LightEntity):
|
||||
class NetatmoLight(NetatmoModuleEntity, LightEntity):
|
||||
"""Representation of a dimmable light by Legrand/BTicino."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
netatmo_device: NetatmoDevice,
|
||||
) -> None:
|
||||
_attr_name = None
|
||||
_attr_configuration_url = CONF_URL_CONTROL
|
||||
_attr_brightness: int | None = 0
|
||||
device: NaModules.NLFN
|
||||
|
||||
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
||||
"""Initialize a Netatmo light."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
self._attr_unique_id = f"{self.device.entity_id}-light"
|
||||
|
||||
self._dimmer = cast(NaModules.NLFN, netatmo_device.device)
|
||||
self._id = self._dimmer.entity_id
|
||||
self._home_id = self._dimmer.home.entity_id
|
||||
self._device_name = self._dimmer.name
|
||||
self._attr_name = f"{self._device_name}"
|
||||
self._model = self._dimmer.device_type
|
||||
self._config_url = CONF_URL_CONTROL
|
||||
self._attr_brightness = 0
|
||||
self._attr_unique_id = f"{self._id}-light"
|
||||
|
||||
if self._dimmer.brightness is not None:
|
||||
if self.device.brightness is not None:
|
||||
self._attr_color_mode = ColorMode.BRIGHTNESS
|
||||
else:
|
||||
self._attr_color_mode = ColorMode.ONOFF
|
||||
self._attr_supported_color_modes = {self._attr_color_mode}
|
||||
|
||||
self._signal_name = f"{HOME}-{self._home_id}"
|
||||
self._signal_name = f"{HOME}-{self.home.entity_id}"
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._dimmer.home.entity_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
},
|
||||
]
|
||||
@ -193,27 +173,27 @@ class NetatmoLight(NetatmoBaseEntity, LightEntity):
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn light on."""
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
await self._dimmer.async_set_brightness(kwargs[ATTR_BRIGHTNESS])
|
||||
await self.device.async_set_brightness(kwargs[ATTR_BRIGHTNESS])
|
||||
|
||||
else:
|
||||
await self._dimmer.async_on()
|
||||
await self.device.async_on()
|
||||
|
||||
self._attr_is_on = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn light off."""
|
||||
await self._dimmer.async_off()
|
||||
await self.device.async_off()
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._attr_is_on = self._dimmer.on is True
|
||||
self._attr_is_on = self.device.on is True
|
||||
|
||||
if self._dimmer.brightness is not None:
|
||||
if (brightness := self.device.brightness) is not None:
|
||||
# Netatmo uses a range of [0, 100] to control brightness
|
||||
self._attr_brightness = round((self._dimmer.brightness / 100) * 255)
|
||||
self._attr_brightness = round((brightness / 100) * 255)
|
||||
else:
|
||||
self._attr_brightness = None
|
||||
|
@ -4,11 +4,10 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pyatmo import DeviceType
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
@ -17,6 +16,7 @@ from .const import (
|
||||
DATA_SCHEDULES,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_SCHEDULE,
|
||||
MANUFACTURER,
|
||||
NETATMO_CREATE_SELECT,
|
||||
)
|
||||
from .data_handler import HOME, SIGNAL_NAME, NetatmoHome
|
||||
@ -43,39 +43,36 @@ async def async_setup_entry(
|
||||
class NetatmoScheduleSelect(NetatmoBaseEntity, SelectEntity):
|
||||
"""Representation a Netatmo thermostat schedule selector."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
netatmo_home: NetatmoHome,
|
||||
) -> None:
|
||||
_attr_name = None
|
||||
|
||||
def __init__(self, netatmo_home: NetatmoHome) -> None:
|
||||
"""Initialize the select entity."""
|
||||
SelectEntity.__init__(self)
|
||||
super().__init__(netatmo_home.data_handler)
|
||||
|
||||
self._home = netatmo_home.home
|
||||
self._home_id = self._home.entity_id
|
||||
self.home = netatmo_home.home
|
||||
|
||||
self._signal_name = netatmo_home.signal_name
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._home.entity_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: netatmo_home.signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self.home.entity_id)},
|
||||
name=self.home.name,
|
||||
manufacturer=MANUFACTURER,
|
||||
model="Climate",
|
||||
configuration_url=CONF_URL_ENERGY,
|
||||
)
|
||||
|
||||
self._device_name = self._home.name
|
||||
self._attr_name = f"{self._device_name}"
|
||||
self._attr_unique_id = f"{self.home.entity_id}-schedule-select"
|
||||
|
||||
self._model = DeviceType.NATherm1
|
||||
self._config_url = CONF_URL_ENERGY
|
||||
|
||||
self._attr_unique_id = f"{self._home_id}-schedule-select"
|
||||
|
||||
self._attr_current_option = getattr(self._home.get_selected_schedule(), "name")
|
||||
self._attr_current_option = getattr(self.home.get_selected_schedule(), "name")
|
||||
self._attr_options = [
|
||||
schedule.name for schedule in self._home.schedules.values()
|
||||
schedule.name for schedule in self.home.schedules.values()
|
||||
]
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
@ -95,12 +92,12 @@ class NetatmoScheduleSelect(NetatmoBaseEntity, SelectEntity):
|
||||
"""Handle webhook events."""
|
||||
data = event["data"]
|
||||
|
||||
if self._home_id != data["home_id"]:
|
||||
if self.home.entity_id != data["home_id"]:
|
||||
return
|
||||
|
||||
if data["event_type"] == EVENT_TYPE_SCHEDULE and "schedule_id" in data:
|
||||
self._attr_current_option = getattr(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id].get(
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self.home.entity_id].get(
|
||||
data["schedule_id"]
|
||||
),
|
||||
"name",
|
||||
@ -110,24 +107,26 @@ class NetatmoScheduleSelect(NetatmoBaseEntity, SelectEntity):
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
for sid, schedule in self.hass.data[DOMAIN][DATA_SCHEDULES][
|
||||
self._home_id
|
||||
self.home.entity_id
|
||||
].items():
|
||||
if schedule.name != option:
|
||||
continue
|
||||
_LOGGER.debug(
|
||||
"Setting %s schedule to %s (%s)",
|
||||
self._home_id,
|
||||
self.home.entity_id,
|
||||
option,
|
||||
sid,
|
||||
)
|
||||
await self._home.async_switch_schedule(schedule_id=sid)
|
||||
await self.home.async_switch_schedule(schedule_id=sid)
|
||||
break
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._attr_current_option = getattr(self._home.get_selected_schedule(), "name")
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self._home_id] = self._home.schedules
|
||||
self._attr_current_option = getattr(self.home.get_selected_schedule(), "name")
|
||||
self.hass.data[DOMAIN][DATA_SCHEDULES][self.home.entity_id] = (
|
||||
self.home.schedules
|
||||
)
|
||||
self._attr_options = [
|
||||
schedule.name for schedule in self._home.schedules.values()
|
||||
schedule.name for schedule in self.home.schedules.values()
|
||||
]
|
||||
|
@ -7,6 +7,7 @@ import logging
|
||||
from typing import cast
|
||||
|
||||
import pyatmo
|
||||
from pyatmo import DeviceType
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
@ -31,7 +32,10 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers.device_registry import async_entries_for_config_entry
|
||||
from homeassistant.helpers.device_registry import (
|
||||
DeviceInfo,
|
||||
async_entries_for_config_entry,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
@ -52,7 +56,7 @@ from .const import (
|
||||
SIGNAL_NAME,
|
||||
)
|
||||
from .data_handler import HOME, PUBLIC, NetatmoDataHandler, NetatmoDevice, NetatmoRoom
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoBaseEntity, NetatmoModuleEntity, NetatmoRoomEntity
|
||||
from .helper import NetatmoArea
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -305,11 +309,9 @@ async def async_setup_entry(
|
||||
netatmo_device.device.name,
|
||||
)
|
||||
async_add_entities(
|
||||
[
|
||||
NetatmoSensor(netatmo_device, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in netatmo_device.device.features
|
||||
]
|
||||
NetatmoSensor(netatmo_device, description)
|
||||
for description in SENSOR_TYPES
|
||||
if description.key in netatmo_device.device.features
|
||||
)
|
||||
|
||||
entry.async_on_unload(
|
||||
@ -395,11 +397,11 @@ async def async_setup_entry(
|
||||
await add_public_entities(False)
|
||||
|
||||
|
||||
class NetatmoWeatherSensor(NetatmoBaseEntity, SensorEntity):
|
||||
class NetatmoWeatherSensor(NetatmoModuleEntity, SensorEntity):
|
||||
"""Implementation of a Netatmo weather/home coach sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: NetatmoSensorEntityDescription
|
||||
_attr_configuration_url = CONF_URL_WEATHER
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -407,16 +409,9 @@ class NetatmoWeatherSensor(NetatmoBaseEntity, SensorEntity):
|
||||
description: NetatmoSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
self.entity_description = description
|
||||
|
||||
self._module = netatmo_device.device
|
||||
self._id = self._module.entity_id
|
||||
self._station_id = (
|
||||
self._module.bridge if self._module.bridge is not None else self._id
|
||||
)
|
||||
self._device_name = self._module.name
|
||||
category = getattr(self._module.device_category, "name")
|
||||
category = getattr(self.device.device_category, "name")
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
@ -425,16 +420,10 @@ class NetatmoWeatherSensor(NetatmoBaseEntity, SensorEntity):
|
||||
},
|
||||
]
|
||||
)
|
||||
self._attr_unique_id = f"{self.device.entity_id}-{description.key}"
|
||||
|
||||
self._attr_name = f"{description.name}"
|
||||
self._model = self._module.device_type
|
||||
self._config_url = CONF_URL_WEATHER
|
||||
self._attr_unique_id = f"{self._id}-{description.key}"
|
||||
|
||||
if hasattr(self._module, "place"):
|
||||
place = cast(
|
||||
pyatmo.modules.base_class.Place, getattr(self._module, "place")
|
||||
)
|
||||
if hasattr(self.device, "place"):
|
||||
place = cast(pyatmo.modules.base_class.Place, getattr(self.device, "place"))
|
||||
if hasattr(place, "location") and place.location is not None:
|
||||
self._attr_extra_state_attributes.update(
|
||||
{
|
||||
@ -443,12 +432,19 @@ class NetatmoWeatherSensor(NetatmoBaseEntity, SensorEntity):
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
def device_type(self) -> DeviceType:
|
||||
"""Return the Netatmo device type."""
|
||||
if "." not in self.device.device_type:
|
||||
return super().device_type
|
||||
return DeviceType(self.device.device_type.partition(".")[2])
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
if (
|
||||
not self._module.reachable
|
||||
or (state := getattr(self._module, self.entity_description.netatmo_name))
|
||||
not self.device.reachable
|
||||
or (state := getattr(self.device, self.entity_description.netatmo_name))
|
||||
is None
|
||||
):
|
||||
if self.available:
|
||||
@ -474,22 +470,18 @@ class NetatmoWeatherSensor(NetatmoBaseEntity, SensorEntity):
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
||||
class NetatmoClimateBatterySensor(NetatmoBaseEntity, SensorEntity):
|
||||
class NetatmoClimateBatterySensor(NetatmoModuleEntity, SensorEntity):
|
||||
"""Implementation of a Netatmo sensor."""
|
||||
|
||||
entity_description: NetatmoSensorEntityDescription
|
||||
device: pyatmo.modules.NRV
|
||||
_attr_configuration_url = CONF_URL_ENERGY
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
netatmo_device: NetatmoDevice,
|
||||
) -> None:
|
||||
def __init__(self, netatmo_device: NetatmoDevice) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
self.entity_description = BATTERY_SENSOR_DESCRIPTION
|
||||
|
||||
self._module = cast(pyatmo.modules.NRV, netatmo_device.device)
|
||||
self._id = netatmo_device.parent_id
|
||||
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
@ -500,31 +492,32 @@ class NetatmoClimateBatterySensor(NetatmoBaseEntity, SensorEntity):
|
||||
]
|
||||
)
|
||||
|
||||
self._attr_name = f"{self._module.name} {self.entity_description.name}"
|
||||
self._room_id = self._module.room_id
|
||||
self._model = getattr(self._module.device_type, "value")
|
||||
self._config_url = CONF_URL_ENERGY
|
||||
|
||||
self._attr_unique_id = (
|
||||
f"{self._id}-{self._module.entity_id}-{self.entity_description.key}"
|
||||
self._attr_unique_id = f"{netatmo_device.parent_id}-{self.device.entity_id}-{self.entity_description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, netatmo_device.parent_id)},
|
||||
name=netatmo_device.device.name,
|
||||
manufacturer=self.device_description[0],
|
||||
model=self.device_description[1],
|
||||
configuration_url=self._attr_configuration_url,
|
||||
)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
if not self._module.reachable:
|
||||
if not self.device.reachable:
|
||||
if self.available:
|
||||
self._attr_available = False
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_native_value = self._module.battery
|
||||
self._attr_native_value = self.device.battery
|
||||
|
||||
|
||||
class NetatmoSensor(NetatmoBaseEntity, SensorEntity):
|
||||
class NetatmoSensor(NetatmoModuleEntity, SensorEntity):
|
||||
"""Implementation of a Netatmo sensor."""
|
||||
|
||||
entity_description: NetatmoSensorEntityDescription
|
||||
_attr_configuration_url = CONF_URL_ENERGY
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -532,40 +525,32 @@ class NetatmoSensor(NetatmoBaseEntity, SensorEntity):
|
||||
description: NetatmoSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
super().__init__(netatmo_device)
|
||||
self.entity_description = description
|
||||
|
||||
self._module = netatmo_device.device
|
||||
self._id = self._module.entity_id
|
||||
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": netatmo_device.device.home.entity_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: netatmo_device.signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self._attr_name = f"{self._module.name} {self.entity_description.name}"
|
||||
self._room_id = self._module.room_id
|
||||
self._model = getattr(self._module.device_type, "value")
|
||||
self._config_url = CONF_URL_ENERGY
|
||||
|
||||
self._attr_unique_id = (
|
||||
f"{self._id}-{self._module.entity_id}-{self.entity_description.key}"
|
||||
f"{self.device.entity_id}-{self.device.entity_id}-{description.key}"
|
||||
)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
if not self._module.reachable:
|
||||
if not self.device.reachable:
|
||||
if self.available:
|
||||
self._attr_available = False
|
||||
return
|
||||
|
||||
if (state := getattr(self._module, self.entity_description.key)) is None:
|
||||
if (state := getattr(self.device, self.entity_description.key)) is None:
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
@ -609,7 +594,7 @@ def process_wifi(strength: int) -> str:
|
||||
return "Full"
|
||||
|
||||
|
||||
class NetatmoRoomSensor(NetatmoBaseEntity, SensorEntity):
|
||||
class NetatmoRoomSensor(NetatmoRoomEntity, SensorEntity):
|
||||
"""Implementation of a Netatmo room sensor."""
|
||||
|
||||
entity_description: NetatmoSensorEntityDescription
|
||||
@ -620,37 +605,27 @@ class NetatmoRoomSensor(NetatmoBaseEntity, SensorEntity):
|
||||
description: NetatmoSensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(netatmo_room.data_handler)
|
||||
super().__init__(netatmo_room)
|
||||
self.entity_description = description
|
||||
|
||||
self._room = netatmo_room.room
|
||||
self._id = self._room.entity_id
|
||||
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": netatmo_room.room.home.entity_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: netatmo_room.signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
self._attr_name = f"{self._room.name} {self.entity_description.name}"
|
||||
self._room_id = self._room.entity_id
|
||||
self._config_url = CONF_URL_ENERGY
|
||||
|
||||
assert self._room.climate_type
|
||||
self._model = self._room.climate_type
|
||||
|
||||
self._attr_unique_id = (
|
||||
f"{self._id}-{self._room.entity_id}-{self.entity_description.key}"
|
||||
f"{self.device.entity_id}-{self.device.entity_id}-{description.key}"
|
||||
)
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
if (state := getattr(self._room, self.entity_description.key)) is None:
|
||||
if (state := getattr(self.device, self.entity_description.key)) is None:
|
||||
return
|
||||
|
||||
self._attr_native_value = state
|
||||
@ -661,7 +636,6 @@ class NetatmoRoomSensor(NetatmoBaseEntity, SensorEntity):
|
||||
class NetatmoPublicSensor(NetatmoBaseEntity, SensorEntity):
|
||||
"""Represent a single sensor in a Netatmo."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: NetatmoSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
@ -691,33 +665,31 @@ class NetatmoPublicSensor(NetatmoBaseEntity, SensorEntity):
|
||||
|
||||
self.area = area
|
||||
self._mode = area.mode
|
||||
self._area_name = area.area_name
|
||||
self._id = self._area_name
|
||||
self._device_name = f"{self._area_name}"
|
||||
self._attr_name = f"{description.name}"
|
||||
self._show_on_map = area.show_on_map
|
||||
self._config_url = CONF_URL_PUBLIC_WEATHER
|
||||
self._attr_unique_id = (
|
||||
f"{self._device_name.replace(' ', '-')}-{description.key}"
|
||||
)
|
||||
self._model = PUBLIC
|
||||
self._attr_unique_id = f"{area.area_name.replace(' ', '-')}-{description.key}"
|
||||
|
||||
self._attr_extra_state_attributes.update(
|
||||
{
|
||||
ATTR_LATITUDE: (self.area.lat_ne + self.area.lat_sw) / 2,
|
||||
ATTR_LONGITUDE: (self.area.lon_ne + self.area.lon_sw) / 2,
|
||||
ATTR_LATITUDE: (area.lat_ne + area.lat_sw) / 2,
|
||||
ATTR_LONGITUDE: (area.lon_ne + area.lon_sw) / 2,
|
||||
}
|
||||
)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, area.area_name)},
|
||||
name=area.area_name,
|
||||
model="Public Weather station",
|
||||
manufacturer="Netatmo",
|
||||
configuration_url=CONF_URL_PUBLIC_WEATHER,
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Entity created."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
assert self.device_info and "name" in self.device_info
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
f"netatmo-config-{self.device_info['name']}",
|
||||
f"netatmo-config-{self.area.area_name}",
|
||||
self.async_config_update_callback,
|
||||
)
|
||||
)
|
||||
@ -776,7 +748,7 @@ class NetatmoPublicSensor(NetatmoBaseEntity, SensorEntity):
|
||||
_LOGGER.error(
|
||||
"No station provides %s data in the area %s",
|
||||
self.entity_description.key,
|
||||
self._area_name,
|
||||
self.area.area_name,
|
||||
)
|
||||
|
||||
self._attr_available = False
|
||||
|
@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import Any
|
||||
|
||||
from pyatmo import modules as NaModules
|
||||
|
||||
@ -15,7 +15,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import CONF_URL_CONTROL, NETATMO_CREATE_SWITCH
|
||||
from .data_handler import HOME, SIGNAL_NAME, NetatmoDevice
|
||||
from .entity import NetatmoBaseEntity
|
||||
from .entity import NetatmoModuleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -38,51 +38,45 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
|
||||
class NetatmoSwitch(NetatmoBaseEntity, SwitchEntity):
|
||||
class NetatmoSwitch(NetatmoModuleEntity, SwitchEntity):
|
||||
"""Representation of a Netatmo switch device."""
|
||||
|
||||
_attr_name = None
|
||||
_attr_configuration_url = CONF_URL_CONTROL
|
||||
device: NaModules.Switch
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
netatmo_device: NetatmoDevice,
|
||||
) -> None:
|
||||
"""Initialize the Netatmo device."""
|
||||
super().__init__(netatmo_device.data_handler)
|
||||
|
||||
self._switch = cast(NaModules.Switch, netatmo_device.device)
|
||||
|
||||
self._id = self._switch.entity_id
|
||||
self._attr_name = self._device_name = self._switch.name
|
||||
self._model = self._switch.device_type
|
||||
self._config_url = CONF_URL_CONTROL
|
||||
|
||||
self._home_id = self._switch.home.entity_id
|
||||
|
||||
self._signal_name = f"{HOME}-{self._home_id}"
|
||||
super().__init__(netatmo_device)
|
||||
self._signal_name = f"{HOME}-{self.home.entity_id}"
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": HOME,
|
||||
"home_id": self._home_id,
|
||||
"home_id": self.home.entity_id,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
self._attr_unique_id = f"{self._id}-{self._model}"
|
||||
self._attr_is_on = self._switch.on
|
||||
self._attr_unique_id = f"{self.device.entity_id}-{self.device_type}"
|
||||
self._attr_is_on = self.device.on
|
||||
|
||||
@callback
|
||||
def async_update_callback(self) -> None:
|
||||
"""Update the entity's state."""
|
||||
self._attr_is_on = self._switch.on
|
||||
self._attr_is_on = self.device.on
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone on."""
|
||||
await self._switch.async_on()
|
||||
await self.device.async_on()
|
||||
self._attr_is_on = True
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the zone off."""
|
||||
await self._switch.async_off()
|
||||
await self.device.async_off()
|
||||
self._attr_is_on = False
|
||||
self.async_write_ha_state()
|
||||
|
@ -26,7 +26,7 @@
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.bureau',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -37,7 +37,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Bureau',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 401>,
|
||||
@ -101,7 +101,7 @@
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.cocina',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -112,7 +112,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Cocina',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 401>,
|
||||
@ -182,7 +182,7 @@
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.corridor',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -193,7 +193,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Corridor',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 401>,
|
||||
@ -262,7 +262,7 @@
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.entrada',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -273,7 +273,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Entrada',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 401>,
|
||||
@ -344,7 +344,7 @@
|
||||
'domain': 'climate',
|
||||
'entity_category': None,
|
||||
'entity_id': 'climate.livingroom',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -355,7 +355,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Livingroom',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <ClimateEntityFeature: 401>,
|
||||
|
@ -12,7 +12,7 @@
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.bubendorff_blind',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -23,7 +23,7 @@
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.SHUTTER: 'shutter'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Bubendorff blind',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
@ -62,7 +62,7 @@
|
||||
'domain': 'cover',
|
||||
'entity_category': None,
|
||||
'entity_id': 'cover.entrance_blinds',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -73,7 +73,7 @@
|
||||
}),
|
||||
'original_device_class': <CoverDeviceClass.SHUTTER: 'shutter'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Entrance Blinds',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <CoverEntityFeature: 15>,
|
||||
|
@ -17,7 +17,7 @@
|
||||
'domain': 'fan',
|
||||
'entity_category': None,
|
||||
'entity_id': 'fan.centralized_ventilation_controler',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -28,7 +28,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Centralized ventilation controler',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': <FanEntityFeature: 8>,
|
||||
|
@ -111,7 +111,7 @@
|
||||
}),
|
||||
'manufacturer': 'Smarther',
|
||||
'model': 'Smarther with Netatmo',
|
||||
'name': '',
|
||||
'name': 'Corridor',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': 'Corridor',
|
||||
@ -141,7 +141,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Energy Meter',
|
||||
'name': '',
|
||||
'name': 'Consumption meter',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -201,7 +201,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Line 1',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -231,7 +231,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Line 2',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -261,7 +261,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Line 3',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -291,7 +291,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Line 4',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -321,7 +321,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Line 5',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -351,7 +351,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Total',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -381,7 +381,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Gas',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -411,7 +411,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Hot water',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -441,7 +441,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Cold water',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -471,7 +471,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Connected Ecometer',
|
||||
'name': '',
|
||||
'name': 'Écocompteur',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -771,7 +771,7 @@
|
||||
}),
|
||||
'manufacturer': 'Legrand',
|
||||
'model': 'Plug',
|
||||
'name': '',
|
||||
'name': 'Prise',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
@ -951,7 +951,7 @@
|
||||
}),
|
||||
'manufacturer': 'Netatmo',
|
||||
'model': 'OpenTherm Modulating Thermostat',
|
||||
'name': '',
|
||||
'name': 'Bureau Modulate',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': 'Bureau',
|
||||
@ -981,7 +981,7 @@
|
||||
}),
|
||||
'manufacturer': 'Netatmo',
|
||||
'model': 'Smart Thermostat',
|
||||
'name': '',
|
||||
'name': 'Livingroom',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': 'Livingroom',
|
||||
@ -1011,7 +1011,7 @@
|
||||
}),
|
||||
'manufacturer': 'Netatmo',
|
||||
'model': 'Smart Valve',
|
||||
'name': '',
|
||||
'name': 'Valve1',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': 'Entrada',
|
||||
@ -1041,7 +1041,7 @@
|
||||
}),
|
||||
'manufacturer': 'Netatmo',
|
||||
'model': 'Smart Valve',
|
||||
'name': '',
|
||||
'name': 'Valve2',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': 'Cocina',
|
||||
@ -1049,6 +1049,36 @@
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[netatmo-91763b24c43d3e344f424e8b]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': 'https://my.netatmo.com/app/energy',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'netatmo',
|
||||
'91763b24c43d3e344f424e8b',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Netatmo',
|
||||
'model': 'Climate',
|
||||
'name': 'MYHOME',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[netatmo-Home avg]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
@ -1109,33 +1139,3 @@
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[netatmo-]
|
||||
DeviceRegistryEntrySnapshot({
|
||||
'area_id': None,
|
||||
'config_entries': <ANY>,
|
||||
'configuration_url': 'https://my.netatmo.com/app/energy',
|
||||
'connections': set({
|
||||
}),
|
||||
'disabled_by': None,
|
||||
'entry_type': None,
|
||||
'hw_version': None,
|
||||
'id': <ANY>,
|
||||
'identifiers': set({
|
||||
tuple(
|
||||
'netatmo',
|
||||
'',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Netatmo',
|
||||
'model': 'Smart Thermostat',
|
||||
'name': 'MYHOME',
|
||||
'name_by_user': None,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
|
@ -16,7 +16,7 @@
|
||||
'domain': 'light',
|
||||
'entity_category': None,
|
||||
'entity_id': 'light.bathroom_light',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -27,7 +27,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Bathroom light',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -127,7 +127,7 @@
|
||||
'domain': 'light',
|
||||
'entity_category': None,
|
||||
'entity_id': 'light.unknown_00_11_22_33_00_11_45_fe',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -138,7 +138,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Unknown 00:11:22:33:00:11:45:fe',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
|
@ -17,7 +17,7 @@
|
||||
'domain': 'select',
|
||||
'entity_category': None,
|
||||
'entity_id': 'select.myhome',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -28,7 +28,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'MYHOME',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
|
@ -936,7 +936,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.bureau_modulate_battery_percent',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -947,7 +947,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Bureau Modulate Battery Percent',
|
||||
'original_name': 'Battery Percent',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -988,7 +988,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.cold_water_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -999,7 +999,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Cold water Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1038,7 +1038,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.cold_water_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1049,7 +1049,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Cold water Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1076,7 +1076,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.consumption_meter_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1087,7 +1087,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Consumption meter Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1126,7 +1126,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.consumption_meter_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1137,7 +1137,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Consumption meter Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1164,7 +1164,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.corridor_humidity',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1175,7 +1175,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.HUMIDITY: 'humidity'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Corridor Humidity',
|
||||
'original_name': 'Humidity',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1216,7 +1216,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.ecocompteur_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1227,7 +1227,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Écocompteur Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1266,7 +1266,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.ecocompteur_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1277,7 +1277,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Écocompteur Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1304,7 +1304,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.gas_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1315,7 +1315,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Gas Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -1354,7 +1354,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.gas_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -1365,7 +1365,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Gas Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -2350,7 +2350,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.hot_water_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -2361,7 +2361,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Hot water Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -2400,7 +2400,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.hot_water_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -2411,7 +2411,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Hot water Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -2893,7 +2893,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.line_1_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -2904,7 +2904,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Line 1 Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -2943,7 +2943,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.line_1_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -2954,7 +2954,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Line 1 Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -2981,7 +2981,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.line_2_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -2992,7 +2992,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Line 2 Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3031,7 +3031,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.line_2_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3042,7 +3042,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Line 2 Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3069,7 +3069,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.line_3_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3080,7 +3080,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Line 3 Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3119,7 +3119,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.line_3_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3130,7 +3130,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Line 3 Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3157,7 +3157,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.line_4_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3168,7 +3168,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Line 4 Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3207,7 +3207,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.line_4_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3218,7 +3218,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Line 4 Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3245,7 +3245,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.line_5_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3256,7 +3256,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Line 5 Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3295,7 +3295,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.line_5_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3306,7 +3306,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Line 5 Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -3333,7 +3333,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.livingroom_battery_percent',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -3344,7 +3344,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Livingroom Battery Percent',
|
||||
'original_name': 'Battery Percent',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -4307,7 +4307,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.prise_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -4318,7 +4318,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Prise Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -4357,7 +4357,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.prise_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -4368,7 +4368,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Prise Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -4395,7 +4395,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.total_power',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -4406,7 +4406,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Total Power',
|
||||
'original_name': 'Power',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -4445,7 +4445,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.total_reachability',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -4456,7 +4456,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': 'mdi:signal',
|
||||
'original_name': 'Total Reachability',
|
||||
'original_name': 'Reachability',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -4483,7 +4483,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.valve1_battery_percent',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -4494,7 +4494,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Valve1 Battery Percent',
|
||||
'original_name': 'Battery Percent',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
@ -4535,7 +4535,7 @@
|
||||
'domain': 'sensor',
|
||||
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
|
||||
'entity_id': 'sensor.valve2_battery_percent',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -4546,7 +4546,7 @@
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.BATTERY: 'battery'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Valve2 Battery Percent',
|
||||
'original_name': 'Battery Percent',
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
|
@ -12,7 +12,7 @@
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.prise',
|
||||
'has_entity_name': False,
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
@ -23,7 +23,7 @@
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Prise',
|
||||
'original_name': None,
|
||||
'platform': 'netatmo',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
|
Loading…
x
Reference in New Issue
Block a user