Netatmo entity renaming and clean up (#75337)
This commit is contained in:
parent
314778cb50
commit
49854b809c
11 changed files with 128 additions and 144 deletions
|
@ -112,6 +112,8 @@ async def async_setup_entry(
|
|||
class NetatmoCamera(NetatmoBase, Camera):
|
||||
"""Representation of a Netatmo camera."""
|
||||
|
||||
_attr_brand = MANUFACTURER
|
||||
_attr_has_entity_name = True
|
||||
_attr_supported_features = CameraEntityFeature.STREAM
|
||||
|
||||
def __init__(
|
||||
|
@ -126,14 +128,13 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||
Camera.__init__(self)
|
||||
super().__init__(data_handler)
|
||||
|
||||
self._data_classes.append(
|
||||
self._publishers.append(
|
||||
{"name": CAMERA_DATA_CLASS_NAME, SIGNAL_NAME: CAMERA_DATA_CLASS_NAME}
|
||||
)
|
||||
|
||||
self._id = camera_id
|
||||
self._home_id = home_id
|
||||
self._device_name = self._data.get_camera(camera_id=camera_id)["name"]
|
||||
self._attr_name = f"{MANUFACTURER} {self._device_name}"
|
||||
self._model = camera_type
|
||||
self._netatmo_type = TYPE_SECURITY
|
||||
self._attr_unique_id = f"{self._id}-{self._model}"
|
||||
|
@ -193,7 +194,7 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||
"""Return data for this entity."""
|
||||
return cast(
|
||||
pyatmo.AsyncCameraData,
|
||||
self.data_handler.data[self._data_classes[0]["name"]],
|
||||
self.data_handler.data[self._publishers[0]["name"]],
|
||||
)
|
||||
|
||||
async def async_camera_image(
|
||||
|
@ -219,11 +220,6 @@ class NetatmoCamera(NetatmoBase, Camera):
|
|||
"""Return True if entity is available."""
|
||||
return bool(self._alim_status == "on" or self._status == "disconnected")
|
||||
|
||||
@property
|
||||
def brand(self) -> str:
|
||||
"""Return the camera brand."""
|
||||
return MANUFACTURER
|
||||
|
||||
@property
|
||||
def motion_detection_enabled(self) -> bool:
|
||||
"""Return the camera motion detection status."""
|
||||
|
|
|
@ -127,7 +127,7 @@ async def async_setup_entry(
|
|||
for home_id in climate_topology.home_ids:
|
||||
signal_name = f"{CLIMATE_STATE_CLASS_NAME}-{home_id}"
|
||||
|
||||
await data_handler.register_data_class(
|
||||
await data_handler.subscribe(
|
||||
CLIMATE_STATE_CLASS_NAME, signal_name, None, home_id=home_id
|
||||
)
|
||||
|
||||
|
@ -185,14 +185,10 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
self._room = room
|
||||
self._id = self._room.entity_id
|
||||
|
||||
self._climate_state_class = (
|
||||
f"{CLIMATE_STATE_CLASS_NAME}-{self._room.home.entity_id}"
|
||||
)
|
||||
self._climate_state: pyatmo.AsyncClimate = data_handler.data[
|
||||
self._climate_state_class
|
||||
]
|
||||
self._signal_name = f"{CLIMATE_STATE_CLASS_NAME}-{self._room.home.entity_id}"
|
||||
self._climate_state: pyatmo.AsyncClimate = data_handler.data[self._signal_name]
|
||||
|
||||
self._data_classes.extend(
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
|
@ -201,7 +197,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
{
|
||||
"name": CLIMATE_STATE_CLASS_NAME,
|
||||
"home_id": self._room.home.entity_id,
|
||||
SIGNAL_NAME: self._climate_state_class,
|
||||
SIGNAL_NAME: self._signal_name,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
@ -254,7 +250,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
self.data_handler,
|
||||
module,
|
||||
self._id,
|
||||
self._climate_state_class,
|
||||
self._signal_name,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -278,7 +274,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
ATTR_SELECTED_SCHEDULE
|
||||
] = self._selected_schedule
|
||||
self.async_write_ha_state()
|
||||
self.data_handler.async_force_update(self._climate_state_class)
|
||||
self.data_handler.async_force_update(self._signal_name)
|
||||
return
|
||||
|
||||
home = data["home"]
|
||||
|
@ -295,7 +291,7 @@ class NetatmoThermostat(NetatmoBase, ClimateEntity):
|
|||
self._attr_target_temperature = self._away_temperature
|
||||
elif self._attr_preset_mode == PRESET_SCHEDULE:
|
||||
self.async_update_callback()
|
||||
self.data_handler.async_force_update(self._climate_state_class)
|
||||
self.data_handler.async_force_update(self._signal_name)
|
||||
self.async_write_ha_state()
|
||||
return
|
||||
|
||||
|
|
|
@ -73,16 +73,16 @@ DATA_HANDLER = "netatmo_data_handler"
|
|||
SIGNAL_NAME = "signal_name"
|
||||
NETATMO_CREATE_BATTERY = "netatmo_create_battery"
|
||||
|
||||
CONF_CLOUDHOOK_URL = "cloudhook_url"
|
||||
CONF_WEATHER_AREAS = "weather_areas"
|
||||
CONF_NEW_AREA = "new_area"
|
||||
CONF_AREA_NAME = "area_name"
|
||||
CONF_CLOUDHOOK_URL = "cloudhook_url"
|
||||
CONF_LAT_NE = "lat_ne"
|
||||
CONF_LON_NE = "lon_ne"
|
||||
CONF_LAT_SW = "lat_sw"
|
||||
CONF_LON_NE = "lon_ne"
|
||||
CONF_LON_SW = "lon_sw"
|
||||
CONF_NEW_AREA = "new_area"
|
||||
CONF_PUBLIC_MODE = "mode"
|
||||
CONF_UUID = "uuid"
|
||||
CONF_WEATHER_AREAS = "weather_areas"
|
||||
|
||||
OAUTH2_AUTHORIZE = "https://api.netatmo.com/oauth2/authorize"
|
||||
OAUTH2_TOKEN = "https://api.netatmo.com/oauth2/token"
|
||||
|
@ -94,53 +94,53 @@ DATA_HOMES = "netatmo_homes"
|
|||
DATA_PERSONS = "netatmo_persons"
|
||||
DATA_SCHEDULES = "netatmo_schedules"
|
||||
|
||||
NETATMO_WEBHOOK_URL = None
|
||||
NETATMO_EVENT = "netatmo_event"
|
||||
NETATMO_WEBHOOK_URL = None
|
||||
|
||||
DEFAULT_PERSON = "unknown"
|
||||
DEFAULT_DISCOVERY = True
|
||||
DEFAULT_PERSON = "unknown"
|
||||
DEFAULT_WEBHOOKS = False
|
||||
|
||||
ATTR_PSEUDO = "pseudo"
|
||||
ATTR_CAMERA_LIGHT_MODE = "camera_light_mode"
|
||||
ATTR_EVENT_TYPE = "event_type"
|
||||
ATTR_FACE_URL = "face_url"
|
||||
ATTR_HEATING_POWER_REQUEST = "heating_power_request"
|
||||
ATTR_HOME_ID = "home_id"
|
||||
ATTR_HOME_NAME = "home_name"
|
||||
ATTR_IS_KNOWN = "is_known"
|
||||
ATTR_PERSON = "person"
|
||||
ATTR_PERSONS = "persons"
|
||||
ATTR_IS_KNOWN = "is_known"
|
||||
ATTR_FACE_URL = "face_url"
|
||||
ATTR_PSEUDO = "pseudo"
|
||||
ATTR_SCHEDULE_ID = "schedule_id"
|
||||
ATTR_SCHEDULE_NAME = "schedule_name"
|
||||
ATTR_SELECTED_SCHEDULE = "selected_schedule"
|
||||
ATTR_CAMERA_LIGHT_MODE = "camera_light_mode"
|
||||
|
||||
SERVICE_SET_CAMERA_LIGHT = "set_camera_light"
|
||||
SERVICE_SET_SCHEDULE = "set_schedule"
|
||||
SERVICE_SET_PERSONS_HOME = "set_persons_home"
|
||||
SERVICE_SET_PERSON_AWAY = "set_person_away"
|
||||
SERVICE_SET_PERSONS_HOME = "set_persons_home"
|
||||
SERVICE_SET_SCHEDULE = "set_schedule"
|
||||
|
||||
# Climate events
|
||||
EVENT_TYPE_SET_POINT = "set_point"
|
||||
EVENT_TYPE_CANCEL_SET_POINT = "cancel_set_point"
|
||||
EVENT_TYPE_THERM_MODE = "therm_mode"
|
||||
EVENT_TYPE_SCHEDULE = "schedule"
|
||||
EVENT_TYPE_SET_POINT = "set_point"
|
||||
EVENT_TYPE_THERM_MODE = "therm_mode"
|
||||
# Camera events
|
||||
EVENT_TYPE_LIGHT_MODE = "light_mode"
|
||||
EVENT_TYPE_CAMERA_OUTDOOR = "outdoor"
|
||||
EVENT_TYPE_CAMERA_ANIMAL = "animal"
|
||||
EVENT_TYPE_CAMERA_HUMAN = "human"
|
||||
EVENT_TYPE_CAMERA_VEHICLE = "vehicle"
|
||||
EVENT_TYPE_CAMERA_MOVEMENT = "movement"
|
||||
EVENT_TYPE_CAMERA_OUTDOOR = "outdoor"
|
||||
EVENT_TYPE_CAMERA_PERSON = "person"
|
||||
EVENT_TYPE_CAMERA_PERSON_AWAY = "person_away"
|
||||
EVENT_TYPE_CAMERA_VEHICLE = "vehicle"
|
||||
EVENT_TYPE_LIGHT_MODE = "light_mode"
|
||||
# Door tags
|
||||
EVENT_TYPE_DOOR_TAG_SMALL_MOVE = "tag_small_move"
|
||||
EVENT_TYPE_ALARM_STARTED = "alarm_started"
|
||||
EVENT_TYPE_DOOR_TAG_BIG_MOVE = "tag_big_move"
|
||||
EVENT_TYPE_DOOR_TAG_OPEN = "tag_open"
|
||||
EVENT_TYPE_DOOR_TAG_SMALL_MOVE = "tag_small_move"
|
||||
EVENT_TYPE_OFF = "off"
|
||||
EVENT_TYPE_ON = "on"
|
||||
EVENT_TYPE_ALARM_STARTED = "alarm_started"
|
||||
|
||||
OUTDOOR_CAMERA_TRIGGERS = [
|
||||
EVENT_TYPE_CAMERA_ANIMAL,
|
||||
|
@ -149,46 +149,46 @@ OUTDOOR_CAMERA_TRIGGERS = [
|
|||
EVENT_TYPE_CAMERA_VEHICLE,
|
||||
]
|
||||
INDOOR_CAMERA_TRIGGERS = [
|
||||
EVENT_TYPE_CAMERA_MOVEMENT,
|
||||
EVENT_TYPE_CAMERA_PERSON,
|
||||
EVENT_TYPE_CAMERA_PERSON_AWAY,
|
||||
EVENT_TYPE_ALARM_STARTED,
|
||||
EVENT_TYPE_CAMERA_MOVEMENT,
|
||||
EVENT_TYPE_CAMERA_PERSON_AWAY,
|
||||
EVENT_TYPE_CAMERA_PERSON,
|
||||
]
|
||||
DOOR_TAG_TRIGGERS = [
|
||||
EVENT_TYPE_DOOR_TAG_SMALL_MOVE,
|
||||
EVENT_TYPE_DOOR_TAG_BIG_MOVE,
|
||||
EVENT_TYPE_DOOR_TAG_OPEN,
|
||||
EVENT_TYPE_DOOR_TAG_SMALL_MOVE,
|
||||
]
|
||||
CLIMATE_TRIGGERS = [
|
||||
EVENT_TYPE_SET_POINT,
|
||||
EVENT_TYPE_CANCEL_SET_POINT,
|
||||
EVENT_TYPE_SET_POINT,
|
||||
EVENT_TYPE_THERM_MODE,
|
||||
]
|
||||
EVENT_ID_MAP = {
|
||||
EVENT_TYPE_CAMERA_MOVEMENT: "device_id",
|
||||
EVENT_TYPE_CAMERA_PERSON: "device_id",
|
||||
EVENT_TYPE_CAMERA_PERSON_AWAY: "device_id",
|
||||
EVENT_TYPE_ALARM_STARTED: "device_id",
|
||||
EVENT_TYPE_CAMERA_ANIMAL: "device_id",
|
||||
EVENT_TYPE_CAMERA_HUMAN: "device_id",
|
||||
EVENT_TYPE_CAMERA_MOVEMENT: "device_id",
|
||||
EVENT_TYPE_CAMERA_OUTDOOR: "device_id",
|
||||
EVENT_TYPE_CAMERA_PERSON_AWAY: "device_id",
|
||||
EVENT_TYPE_CAMERA_PERSON: "device_id",
|
||||
EVENT_TYPE_CAMERA_VEHICLE: "device_id",
|
||||
EVENT_TYPE_DOOR_TAG_SMALL_MOVE: "device_id",
|
||||
EVENT_TYPE_CANCEL_SET_POINT: "room_id",
|
||||
EVENT_TYPE_DOOR_TAG_BIG_MOVE: "device_id",
|
||||
EVENT_TYPE_DOOR_TAG_OPEN: "device_id",
|
||||
EVENT_TYPE_DOOR_TAG_SMALL_MOVE: "device_id",
|
||||
EVENT_TYPE_LIGHT_MODE: "device_id",
|
||||
EVENT_TYPE_ALARM_STARTED: "device_id",
|
||||
EVENT_TYPE_CANCEL_SET_POINT: "room_id",
|
||||
EVENT_TYPE_SET_POINT: "room_id",
|
||||
EVENT_TYPE_THERM_MODE: "home_id",
|
||||
}
|
||||
|
||||
MODE_LIGHT_ON = "on"
|
||||
MODE_LIGHT_OFF = "off"
|
||||
MODE_LIGHT_AUTO = "auto"
|
||||
MODE_LIGHT_OFF = "off"
|
||||
MODE_LIGHT_ON = "on"
|
||||
CAMERA_LIGHT_MODES = [MODE_LIGHT_ON, MODE_LIGHT_OFF, MODE_LIGHT_AUTO]
|
||||
|
||||
WEBHOOK_ACTIVATION = "webhook_activation"
|
||||
WEBHOOK_DEACTIVATION = "webhook_deactivation"
|
||||
WEBHOOK_LIGHT_MODE = "NOC-light_mode"
|
||||
WEBHOOK_NACAMERA_CONNECTION = "NACamera-connection"
|
||||
WEBHOOK_PUSH_TYPE = "push_type"
|
||||
WEBHOOK_LIGHT_MODE = "NOC-light_mode"
|
||||
|
|
|
@ -64,11 +64,11 @@ class NetatmoDevice:
|
|||
data_handler: NetatmoDataHandler
|
||||
device: pyatmo.climate.NetatmoModule
|
||||
parent_id: str
|
||||
state_class_name: str
|
||||
signal_name: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetatmoDataClass:
|
||||
class NetatmoPublisher:
|
||||
"""Class for keeping track of Netatmo data class metadata."""
|
||||
|
||||
name: str
|
||||
|
@ -85,7 +85,7 @@ class NetatmoDataHandler:
|
|||
self.hass = hass
|
||||
self.config_entry = config_entry
|
||||
self._auth = hass.data[DOMAIN][config_entry.entry_id][AUTH]
|
||||
self.data_classes: dict = {}
|
||||
self.publisher: dict[str, NetatmoPublisher] = {}
|
||||
self.data: dict = {}
|
||||
self._queue: deque = deque()
|
||||
self._webhook: bool = False
|
||||
|
@ -107,7 +107,7 @@ class NetatmoDataHandler:
|
|||
|
||||
await asyncio.gather(
|
||||
*[
|
||||
self.register_data_class(data_class, data_class, None)
|
||||
self.subscribe(data_class, data_class, None)
|
||||
for data_class in (
|
||||
CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
CAMERA_DATA_CLASS_NAME,
|
||||
|
@ -128,20 +128,18 @@ class NetatmoDataHandler:
|
|||
if data_class.next_scan > time():
|
||||
continue
|
||||
|
||||
if data_class_name := data_class.name:
|
||||
self.data_classes[data_class_name].next_scan = (
|
||||
time() + data_class.interval
|
||||
)
|
||||
if publisher := data_class.name:
|
||||
self.publisher[publisher].next_scan = time() + data_class.interval
|
||||
|
||||
await self.async_fetch_data(data_class_name)
|
||||
await self.async_fetch_data(publisher)
|
||||
|
||||
self._queue.rotate(BATCH_SIZE)
|
||||
|
||||
@callback
|
||||
def async_force_update(self, data_class_entry: str) -> None:
|
||||
def async_force_update(self, signal_name: str) -> None:
|
||||
"""Prioritize data retrieval for given data class entry."""
|
||||
self.data_classes[data_class_entry].next_scan = time()
|
||||
self._queue.rotate(-(self._queue.index(self.data_classes[data_class_entry])))
|
||||
self.publisher[signal_name].next_scan = time()
|
||||
self._queue.rotate(-(self._queue.index(self.publisher[signal_name])))
|
||||
|
||||
async def handle_event(self, event: dict) -> None:
|
||||
"""Handle webhook events."""
|
||||
|
@ -157,17 +155,17 @@ class NetatmoDataHandler:
|
|||
_LOGGER.debug("%s camera reconnected", MANUFACTURER)
|
||||
self.async_force_update(CAMERA_DATA_CLASS_NAME)
|
||||
|
||||
async def async_fetch_data(self, data_class_entry: str) -> None:
|
||||
async def async_fetch_data(self, signal_name: str) -> None:
|
||||
"""Fetch data and notify."""
|
||||
if self.data[data_class_entry] is None:
|
||||
if self.data[signal_name] is None:
|
||||
return
|
||||
|
||||
try:
|
||||
await self.data[data_class_entry].async_update()
|
||||
await self.data[signal_name].async_update()
|
||||
|
||||
except pyatmo.NoDevice as err:
|
||||
_LOGGER.debug(err)
|
||||
self.data[data_class_entry] = None
|
||||
self.data[signal_name] = None
|
||||
|
||||
except pyatmo.ApiError as err:
|
||||
_LOGGER.debug(err)
|
||||
|
@ -176,56 +174,52 @@ class NetatmoDataHandler:
|
|||
_LOGGER.debug(err)
|
||||
return
|
||||
|
||||
for update_callback in self.data_classes[data_class_entry].subscriptions:
|
||||
for update_callback in self.publisher[signal_name].subscriptions:
|
||||
if update_callback:
|
||||
update_callback()
|
||||
|
||||
async def register_data_class(
|
||||
async def subscribe(
|
||||
self,
|
||||
data_class_name: str,
|
||||
data_class_entry: str,
|
||||
publisher: str,
|
||||
signal_name: str,
|
||||
update_callback: CALLBACK_TYPE | None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Register data class."""
|
||||
if data_class_entry in self.data_classes:
|
||||
if update_callback not in self.data_classes[data_class_entry].subscriptions:
|
||||
self.data_classes[data_class_entry].subscriptions.append(
|
||||
update_callback
|
||||
)
|
||||
"""Subscribe to publisher."""
|
||||
if signal_name in self.publisher:
|
||||
if update_callback not in self.publisher[signal_name].subscriptions:
|
||||
self.publisher[signal_name].subscriptions.append(update_callback)
|
||||
return
|
||||
|
||||
self.data_classes[data_class_entry] = NetatmoDataClass(
|
||||
name=data_class_entry,
|
||||
interval=DEFAULT_INTERVALS[data_class_name],
|
||||
next_scan=time() + DEFAULT_INTERVALS[data_class_name],
|
||||
self.publisher[signal_name] = NetatmoPublisher(
|
||||
name=signal_name,
|
||||
interval=DEFAULT_INTERVALS[publisher],
|
||||
next_scan=time() + DEFAULT_INTERVALS[publisher],
|
||||
subscriptions=[update_callback],
|
||||
)
|
||||
|
||||
self.data[data_class_entry] = DATA_CLASSES[data_class_name](
|
||||
self._auth, **kwargs
|
||||
)
|
||||
self.data[signal_name] = DATA_CLASSES[publisher](self._auth, **kwargs)
|
||||
|
||||
try:
|
||||
await self.async_fetch_data(data_class_entry)
|
||||
await self.async_fetch_data(signal_name)
|
||||
except KeyError:
|
||||
self.data_classes.pop(data_class_entry)
|
||||
self.publisher.pop(signal_name)
|
||||
raise
|
||||
|
||||
self._queue.append(self.data_classes[data_class_entry])
|
||||
_LOGGER.debug("Data class %s added", data_class_entry)
|
||||
self._queue.append(self.publisher[signal_name])
|
||||
_LOGGER.debug("Publisher %s added", signal_name)
|
||||
|
||||
async def unregister_data_class(
|
||||
self, data_class_entry: str, update_callback: CALLBACK_TYPE | None
|
||||
async def unsubscribe(
|
||||
self, signal_name: str, update_callback: CALLBACK_TYPE | None
|
||||
) -> None:
|
||||
"""Unregister data class."""
|
||||
self.data_classes[data_class_entry].subscriptions.remove(update_callback)
|
||||
"""Unsubscribe from publisher."""
|
||||
self.publisher[signal_name].subscriptions.remove(update_callback)
|
||||
|
||||
if not self.data_classes[data_class_entry].subscriptions:
|
||||
self._queue.remove(self.data_classes[data_class_entry])
|
||||
self.data_classes.pop(data_class_entry)
|
||||
self.data.pop(data_class_entry)
|
||||
_LOGGER.debug("Data class %s removed", data_class_entry)
|
||||
if not self.publisher[signal_name].subscriptions:
|
||||
self._queue.remove(self.publisher[signal_name])
|
||||
self.publisher.pop(signal_name)
|
||||
self.data.pop(signal_name)
|
||||
_LOGGER.debug("Publisher %s removed", signal_name)
|
||||
|
||||
@property
|
||||
def webhook(self) -> bool:
|
||||
|
|
|
@ -17,7 +17,6 @@ from .const import (
|
|||
DATA_HANDLER,
|
||||
DOMAIN,
|
||||
EVENT_TYPE_LIGHT_MODE,
|
||||
MANUFACTURER,
|
||||
SIGNAL_NAME,
|
||||
TYPE_SECURITY,
|
||||
WEBHOOK_LIGHT_MODE,
|
||||
|
@ -63,6 +62,7 @@ class NetatmoLight(NetatmoBase, LightEntity):
|
|||
"""Representation of a Netatmo Presence camera light."""
|
||||
|
||||
_attr_color_mode = ColorMode.ONOFF
|
||||
_attr_has_entity_name = True
|
||||
_attr_supported_color_modes = {ColorMode.ONOFF}
|
||||
|
||||
def __init__(
|
||||
|
@ -76,7 +76,7 @@ class NetatmoLight(NetatmoBase, LightEntity):
|
|||
LightEntity.__init__(self)
|
||||
super().__init__(data_handler)
|
||||
|
||||
self._data_classes.append(
|
||||
self._publishers.append(
|
||||
{"name": CAMERA_DATA_CLASS_NAME, SIGNAL_NAME: CAMERA_DATA_CLASS_NAME}
|
||||
)
|
||||
self._id = camera_id
|
||||
|
@ -84,7 +84,6 @@ class NetatmoLight(NetatmoBase, LightEntity):
|
|||
self._model = camera_type
|
||||
self._netatmo_type = TYPE_SECURITY
|
||||
self._device_name: str = self._data.get_camera(camera_id)["name"]
|
||||
self._attr_name = f"{MANUFACTURER} {self._device_name}"
|
||||
self._is_on = False
|
||||
self._attr_unique_id = f"{self._id}-light"
|
||||
|
||||
|
@ -123,7 +122,7 @@ class NetatmoLight(NetatmoBase, LightEntity):
|
|||
"""Return data for this entity."""
|
||||
return cast(
|
||||
pyatmo.AsyncCameraData,
|
||||
self.data_handler.data[self._data_classes[0]["name"]],
|
||||
self.data_handler.data[self._publishers[0]["name"]],
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
"""Base class for Netatmo entities."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
@ -23,7 +25,7 @@ class NetatmoBase(Entity):
|
|||
def __init__(self, data_handler: NetatmoDataHandler) -> None:
|
||||
"""Set up Netatmo entity base."""
|
||||
self.data_handler = data_handler
|
||||
self._data_classes: list[dict] = []
|
||||
self._publishers: list[dict[str, Any]] = []
|
||||
|
||||
self._device_name: str = ""
|
||||
self._id: str = ""
|
||||
|
@ -35,11 +37,11 @@ class NetatmoBase(Entity):
|
|||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Entity created."""
|
||||
for data_class in self._data_classes:
|
||||
for data_class in self._publishers:
|
||||
signal_name = data_class[SIGNAL_NAME]
|
||||
|
||||
if "home_id" in data_class:
|
||||
await self.data_handler.register_data_class(
|
||||
await self.data_handler.subscribe(
|
||||
data_class["name"],
|
||||
signal_name,
|
||||
self.async_update_callback,
|
||||
|
@ -47,7 +49,7 @@ class NetatmoBase(Entity):
|
|||
)
|
||||
|
||||
elif data_class["name"] == PUBLICDATA_DATA_CLASS_NAME:
|
||||
await self.data_handler.register_data_class(
|
||||
await self.data_handler.subscribe(
|
||||
data_class["name"],
|
||||
signal_name,
|
||||
self.async_update_callback,
|
||||
|
@ -58,13 +60,13 @@ class NetatmoBase(Entity):
|
|||
)
|
||||
|
||||
else:
|
||||
await self.data_handler.register_data_class(
|
||||
await self.data_handler.subscribe(
|
||||
data_class["name"], signal_name, self.async_update_callback
|
||||
)
|
||||
|
||||
for sub in self.data_handler.data_classes[signal_name].subscriptions:
|
||||
for sub in self.data_handler.publisher[signal_name].subscriptions:
|
||||
if sub is None:
|
||||
await self.data_handler.unregister_data_class(signal_name, None)
|
||||
await self.data_handler.unsubscribe(signal_name, None)
|
||||
|
||||
registry = dr.async_get(self.hass)
|
||||
if device := registry.async_get_device({(DOMAIN, self._id)}):
|
||||
|
@ -76,8 +78,8 @@ class NetatmoBase(Entity):
|
|||
"""Run when entity will be removed from hass."""
|
||||
await super().async_will_remove_from_hass()
|
||||
|
||||
for data_class in self._data_classes:
|
||||
await self.data_handler.unregister_data_class(
|
||||
for data_class in self._publishers:
|
||||
await self.data_handler.unsubscribe(
|
||||
data_class[SIGNAL_NAME], self.async_update_callback
|
||||
)
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ async def async_setup_entry(
|
|||
for home_id in climate_topology.home_ids:
|
||||
signal_name = f"{CLIMATE_STATE_CLASS_NAME}-{home_id}"
|
||||
|
||||
await data_handler.register_data_class(
|
||||
await data_handler.subscribe(
|
||||
CLIMATE_STATE_CLASS_NAME, signal_name, None, home_id=home_id
|
||||
)
|
||||
|
||||
|
@ -92,7 +92,7 @@ class NetatmoScheduleSelect(NetatmoBase, SelectEntity):
|
|||
|
||||
self._home = self._climate_state.homes[self._home_id]
|
||||
|
||||
self._data_classes.extend(
|
||||
self._publishers.extend(
|
||||
[
|
||||
{
|
||||
"name": CLIMATE_TOPOLOGY_CLASS_NAME,
|
||||
|
|
|
@ -41,7 +41,6 @@ from .const import (
|
|||
CONF_WEATHER_AREAS,
|
||||
DATA_HANDLER,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
NETATMO_CREATE_BATTERY,
|
||||
SIGNAL_NAME,
|
||||
TYPE_WEATHER,
|
||||
|
@ -422,7 +421,7 @@ async def async_setup_entry(
|
|||
)
|
||||
continue
|
||||
|
||||
await data_handler.register_data_class(
|
||||
await data_handler.subscribe(
|
||||
PUBLICDATA_DATA_CLASS_NAME,
|
||||
signal_name,
|
||||
None,
|
||||
|
@ -487,9 +486,7 @@ class NetatmoSensor(NetatmoBase, SensorEntity):
|
|||
super().__init__(data_handler)
|
||||
self.entity_description = description
|
||||
|
||||
self._data_classes.append(
|
||||
{"name": data_class_name, SIGNAL_NAME: data_class_name}
|
||||
)
|
||||
self._publishers.append({"name": data_class_name, SIGNAL_NAME: data_class_name})
|
||||
|
||||
self._id = module_info["_id"]
|
||||
self._station_id = module_info.get("main_device", self._id)
|
||||
|
@ -507,7 +504,7 @@ class NetatmoSensor(NetatmoBase, SensorEntity):
|
|||
f"{module_info.get('module_name', device['type'])}"
|
||||
)
|
||||
|
||||
self._attr_name = f"{MANUFACTURER} {self._device_name} {description.name}"
|
||||
self._attr_name = f"{self._device_name} {description.name}"
|
||||
self._model = device["type"]
|
||||
self._netatmo_type = TYPE_WEATHER
|
||||
self._attr_unique_id = f"{self._id}-{description.key}"
|
||||
|
@ -517,7 +514,7 @@ class NetatmoSensor(NetatmoBase, SensorEntity):
|
|||
"""Return data for this entity."""
|
||||
return cast(
|
||||
pyatmo.AsyncWeatherStationData,
|
||||
self.data_handler.data[self._data_classes[0]["name"]],
|
||||
self.data_handler.data[self._publishers[0]["name"]],
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -598,7 +595,7 @@ class NetatmoClimateBatterySensor(NetatmoBase, SensorEntity):
|
|||
self._id = netatmo_device.parent_id
|
||||
self._attr_name = f"{self._module.name} {self.entity_description.name}"
|
||||
|
||||
self._state_class_name = netatmo_device.state_class_name
|
||||
self._signal_name = netatmo_device.signal_name
|
||||
self._room_id = self._module.room_id
|
||||
self._model = getattr(self._module.device_type, "value")
|
||||
|
||||
|
@ -734,7 +731,7 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity):
|
|||
|
||||
self._signal_name = f"{PUBLICDATA_DATA_CLASS_NAME}-{area.uuid}"
|
||||
|
||||
self._data_classes.append(
|
||||
self._publishers.append(
|
||||
{
|
||||
"name": PUBLICDATA_DATA_CLASS_NAME,
|
||||
"lat_ne": area.lat_ne,
|
||||
|
@ -751,7 +748,7 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity):
|
|||
self._area_name = area.area_name
|
||||
self._id = self._area_name
|
||||
self._device_name = f"{self._area_name}"
|
||||
self._attr_name = f"{MANUFACTURER} {self._device_name} {description.name}"
|
||||
self._attr_name = f"{self._device_name} {description.name}"
|
||||
self._show_on_map = area.show_on_map
|
||||
self._attr_unique_id = (
|
||||
f"{self._device_name.replace(' ', '-')}-{description.key}"
|
||||
|
@ -788,13 +785,13 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity):
|
|||
if self.area == area:
|
||||
return
|
||||
|
||||
await self.data_handler.unregister_data_class(
|
||||
await self.data_handler.unsubscribe(
|
||||
self._signal_name, self.async_update_callback
|
||||
)
|
||||
|
||||
self.area = area
|
||||
self._signal_name = f"{PUBLICDATA_DATA_CLASS_NAME}-{area.uuid}"
|
||||
self._data_classes = [
|
||||
self._publishers = [
|
||||
{
|
||||
"name": PUBLICDATA_DATA_CLASS_NAME,
|
||||
"lat_ne": area.lat_ne,
|
||||
|
@ -807,7 +804,7 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity):
|
|||
]
|
||||
self._mode = area.mode
|
||||
self._show_on_map = area.show_on_map
|
||||
await self.data_handler.register_data_class(
|
||||
await self.data_handler.subscribe(
|
||||
PUBLICDATA_DATA_CLASS_NAME,
|
||||
self._signal_name,
|
||||
self.async_update_callback,
|
||||
|
|
|
@ -32,8 +32,8 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth):
|
|||
webhook_id = config_entry.data[CONF_WEBHOOK_ID]
|
||||
await hass.async_block_till_done()
|
||||
|
||||
camera_entity_indoor = "camera.netatmo_hall"
|
||||
camera_entity_outdoor = "camera.netatmo_garden"
|
||||
camera_entity_indoor = "camera.hall"
|
||||
camera_entity_outdoor = "camera.garden"
|
||||
assert hass.states.get(camera_entity_indoor).state == "streaming"
|
||||
response = {
|
||||
"event_type": "off",
|
||||
|
@ -95,7 +95,7 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth):
|
|||
|
||||
with patch("pyatmo.camera.AsyncCameraData.async_set_state") as mock_set_state:
|
||||
await hass.services.async_call(
|
||||
"camera", "turn_off", service_data={"entity_id": "camera.netatmo_hall"}
|
||||
"camera", "turn_off", service_data={"entity_id": "camera.hall"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_set_state.assert_called_once_with(
|
||||
|
@ -106,7 +106,7 @@ async def test_setup_component_with_webhook(hass, config_entry, netatmo_auth):
|
|||
|
||||
with patch("pyatmo.camera.AsyncCameraData.async_set_state") as mock_set_state:
|
||||
await hass.services.async_call(
|
||||
"camera", "turn_on", service_data={"entity_id": "camera.netatmo_hall"}
|
||||
"camera", "turn_on", service_data={"entity_id": "camera.hall"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_set_state.assert_called_once_with(
|
||||
|
@ -130,7 +130,7 @@ async def test_camera_image_local(hass, config_entry, requests_mock, netatmo_aut
|
|||
|
||||
uri = "http://192.168.0.123/678460a0d47e5618699fb31169e2b47d"
|
||||
stream_uri = uri + "/live/files/high/index.m3u8"
|
||||
camera_entity_indoor = "camera.netatmo_hall"
|
||||
camera_entity_indoor = "camera.hall"
|
||||
cam = hass.states.get(camera_entity_indoor)
|
||||
|
||||
assert cam is not None
|
||||
|
@ -161,7 +161,7 @@ async def test_camera_image_vpn(hass, config_entry, requests_mock, netatmo_auth)
|
|||
"6d278460699e56180d47ab47169efb31/MpEylTU2MDYzNjRVD-LJxUnIndumKzLboeAwMDqTTw,,"
|
||||
)
|
||||
stream_uri = uri + "/live/files/high/index.m3u8"
|
||||
camera_entity_indoor = "camera.netatmo_garden"
|
||||
camera_entity_indoor = "camera.garden"
|
||||
cam = hass.states.get(camera_entity_indoor)
|
||||
|
||||
assert cam is not None
|
||||
|
@ -188,7 +188,7 @@ async def test_service_set_person_away(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
data = {
|
||||
"entity_id": "camera.netatmo_hall",
|
||||
"entity_id": "camera.hall",
|
||||
"person": "Richard Doe",
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ async def test_service_set_person_away(hass, config_entry, netatmo_auth):
|
|||
)
|
||||
|
||||
data = {
|
||||
"entity_id": "camera.netatmo_hall",
|
||||
"entity_id": "camera.hall",
|
||||
}
|
||||
|
||||
with patch(
|
||||
|
@ -231,7 +231,7 @@ async def test_service_set_person_away_invalid_person(hass, config_entry, netatm
|
|||
await hass.async_block_till_done()
|
||||
|
||||
data = {
|
||||
"entity_id": "camera.netatmo_hall",
|
||||
"entity_id": "camera.hall",
|
||||
"person": "Batman",
|
||||
}
|
||||
|
||||
|
@ -259,7 +259,7 @@ async def test_service_set_persons_home_invalid_person(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
data = {
|
||||
"entity_id": "camera.netatmo_hall",
|
||||
"entity_id": "camera.hall",
|
||||
"persons": "Batman",
|
||||
}
|
||||
|
||||
|
@ -285,7 +285,7 @@ async def test_service_set_persons_home(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
data = {
|
||||
"entity_id": "camera.netatmo_hall",
|
||||
"entity_id": "camera.hall",
|
||||
"persons": "John Doe",
|
||||
}
|
||||
|
||||
|
@ -312,7 +312,7 @@ async def test_service_set_camera_light(hass, config_entry, netatmo_auth):
|
|||
await hass.async_block_till_done()
|
||||
|
||||
data = {
|
||||
"entity_id": "camera.netatmo_garden",
|
||||
"entity_id": "camera.garden",
|
||||
"camera_light_mode": "on",
|
||||
}
|
||||
|
||||
|
@ -485,7 +485,7 @@ async def test_camera_image_raises_exception(hass, config_entry, requests_mock):
|
|||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
camera_entity_indoor = "camera.netatmo_hall"
|
||||
camera_entity_indoor = "camera.hall"
|
||||
|
||||
with pytest.raises(Exception) as excinfo:
|
||||
await camera.async_get_image(hass, camera_entity_indoor)
|
||||
|
|
|
@ -27,7 +27,7 @@ async def test_light_setup_and_services(hass, config_entry, netatmo_auth):
|
|||
await simulate_webhook(hass, webhook_id, FAKE_WEBHOOK_ACTIVATION)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
light_entity = "light.netatmo_garden"
|
||||
light_entity = "light.garden"
|
||||
assert hass.states.get(light_entity).state == "unavailable"
|
||||
|
||||
# Trigger light mode change
|
||||
|
|
|
@ -17,7 +17,7 @@ async def test_weather_sensor(hass, config_entry, netatmo_auth):
|
|||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
prefix = "sensor.netatmo_mystation_"
|
||||
prefix = "sensor.mystation_"
|
||||
|
||||
assert hass.states.get(f"{prefix}temperature").state == "24.6"
|
||||
assert hass.states.get(f"{prefix}humidity").state == "36"
|
||||
|
@ -34,13 +34,13 @@ async def test_public_weather_sensor(hass, config_entry, netatmo_auth):
|
|||
|
||||
assert len(hass.states.async_all()) > 0
|
||||
|
||||
prefix = "sensor.netatmo_home_max_"
|
||||
prefix = "sensor.home_max_"
|
||||
|
||||
assert hass.states.get(f"{prefix}temperature").state == "27.4"
|
||||
assert hass.states.get(f"{prefix}humidity").state == "76"
|
||||
assert hass.states.get(f"{prefix}pressure").state == "1014.4"
|
||||
|
||||
prefix = "sensor.netatmo_home_avg_"
|
||||
prefix = "sensor.home_avg_"
|
||||
|
||||
assert hass.states.get(f"{prefix}temperature").state == "22.7"
|
||||
assert hass.states.get(f"{prefix}humidity").state == "63.2"
|
||||
|
|
Loading…
Add table
Reference in a new issue