Skip to content

Commit

Permalink
Add more base entities to netatmo (home-assistant#107862)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Tobias Sauerwein <[email protected]>
  • Loading branch information
3 people authored Apr 7, 2024
1 parent 9f2fa7e commit 021eed6
Show file tree
Hide file tree
Showing 18 changed files with 483 additions and 511 deletions.
101 changes: 50 additions & 51 deletions homeassistant/components/netatmo/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
WEBHOOK_PUSH_TYPE,
)
from .data_handler import EVENT, HOME, SIGNAL_NAME, NetatmoDevice
from .entity import NetatmoBaseEntity
from .entity import NetatmoModuleEntity

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -80,43 +80,39 @@ def _create_entity(netatmo_device: NetatmoDevice) -> None:
)


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,
netatmo_device: NetatmoDevice,
) -> None:
"""Set up for access to the Netatmo camera images."""
Camera.__init__(self)
super().__init__(netatmo_device.data_handler)

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
super().__init__(netatmo_device)

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}",
},
]
)
Expand All @@ -134,7 +130,7 @@ async def async_added_to_hass(self) -> None:
)
)

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:
Expand All @@ -144,7 +140,10 @@ def handle_event(self, event: dict) -> None:
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
Expand All @@ -168,7 +167,7 @@ async def async_camera_image(
) -> 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,
Expand All @@ -183,50 +182,50 @@ async def async_camera_image(
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,
}
)
Expand All @@ -249,9 +248,9 @@ def process_events(self, event_list: list[NaEvent]) -> dict:

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."""
Expand All @@ -260,7 +259,7 @@ def fetch_person_ids(self, persons: list[str | None]) -> list[str]:

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
Expand All @@ -279,7 +278,7 @@ async def _service_set_persons_home(self, **kwargs: Any) -> None:
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:
Expand All @@ -288,7 +287,7 @@ async def _service_set_person_away(self, **kwargs: Any) -> None:
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,
)

Expand All @@ -299,11 +298,11 @@ async def _service_set_person_away(self, **kwargs: Any) -> None:

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)
Loading

0 comments on commit 021eed6

Please sign in to comment.