Add foscam coordinator (#92665)

* Add foscam coordinator

* Code cleanup

* Coordinator cleanup

* Coordinator cleanup

* Replace async_timeout with asyncio.timeout

* Ignore coordinator (requires external device)

---------

Co-authored-by: G Johansson <goran.johansson@shiftit.se>
This commit is contained in:
Kristof Mariën 2023-12-22 17:44:52 +01:00 committed by GitHub
parent 181190d22d
commit 32a5345a85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 19 deletions

View File

@ -421,6 +421,7 @@ omit =
homeassistant/components/fortios/device_tracker.py homeassistant/components/fortios/device_tracker.py
homeassistant/components/foscam/__init__.py homeassistant/components/foscam/__init__.py
homeassistant/components/foscam/camera.py homeassistant/components/foscam/camera.py
homeassistant/components/foscam/coordinator.py
homeassistant/components/foursquare/* homeassistant/components/foursquare/*
homeassistant/components/free_mobile/notify.py homeassistant/components/free_mobile/notify.py
homeassistant/components/freebox/camera.py homeassistant/components/freebox/camera.py

View File

@ -418,8 +418,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/forked_daapd/ @uvjustin /homeassistant/components/forked_daapd/ @uvjustin
/tests/components/forked_daapd/ @uvjustin /tests/components/forked_daapd/ @uvjustin
/homeassistant/components/fortios/ @kimfrellsen /homeassistant/components/fortios/ @kimfrellsen
/homeassistant/components/foscam/ @skgsergio /homeassistant/components/foscam/ @skgsergio @krmarien
/tests/components/foscam/ @skgsergio /tests/components/foscam/ @skgsergio @krmarien
/homeassistant/components/freebox/ @hacf-fr @Quentame /homeassistant/components/freebox/ @hacf-fr @Quentame
/tests/components/freebox/ @hacf-fr @Quentame /tests/components/freebox/ @hacf-fr @Quentame
/homeassistant/components/freedompro/ @stefano055415 /homeassistant/components/freedompro/ @stefano055415

View File

@ -15,15 +15,28 @@ from homeassistant.helpers.entity_registry import async_migrate_entries
from .config_flow import DEFAULT_RTSP_PORT from .config_flow import DEFAULT_RTSP_PORT
from .const import CONF_RTSP_PORT, DOMAIN, LOGGER, SERVICE_PTZ, SERVICE_PTZ_PRESET from .const import CONF_RTSP_PORT, DOMAIN, LOGGER, SERVICE_PTZ, SERVICE_PTZ_PRESET
from .coordinator import FoscamCoordinator
PLATFORMS = [Platform.CAMERA] PLATFORMS = [Platform.CAMERA]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up foscam from a config entry.""" """Set up foscam from a config entry."""
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = entry.data session = FoscamCamera(
entry.data[CONF_HOST],
entry.data[CONF_PORT],
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
verbose=False,
)
coordinator = FoscamCoordinator(hass, session)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True

View File

@ -3,16 +3,16 @@ from __future__ import annotations
import asyncio import asyncio
from libpyfoscam import FoscamCamera
import voluptuous as vol import voluptuous as vol
from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.camera import Camera, CameraEntityFeature
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import ( from .const import (
CONF_RTSP_PORT, CONF_RTSP_PORT,
@ -22,6 +22,7 @@ from .const import (
SERVICE_PTZ, SERVICE_PTZ,
SERVICE_PTZ_PRESET, SERVICE_PTZ_PRESET,
) )
from .coordinator import FoscamCoordinator
DIR_UP = "up" DIR_UP = "up"
DIR_DOWN = "down" DIR_DOWN = "down"
@ -88,28 +89,27 @@ async def async_setup_entry(
"async_perform_ptz_preset", "async_perform_ptz_preset",
) )
camera = FoscamCamera( coordinator: FoscamCoordinator = hass.data[DOMAIN][config_entry.entry_id]
config_entry.data[CONF_HOST],
config_entry.data[CONF_PORT],
config_entry.data[CONF_USERNAME],
config_entry.data[CONF_PASSWORD],
verbose=False,
)
async_add_entities([HassFoscamCamera(camera, config_entry)]) async_add_entities([HassFoscamCamera(coordinator, config_entry)])
class HassFoscamCamera(Camera): class HassFoscamCamera(CoordinatorEntity[FoscamCoordinator], Camera):
"""An implementation of a Foscam IP camera.""" """An implementation of a Foscam IP camera."""
_attr_has_entity_name = True _attr_has_entity_name = True
_attr_name = None _attr_name = None
def __init__(self, camera: FoscamCamera, config_entry: ConfigEntry) -> None: def __init__(
self,
coordinator: FoscamCoordinator,
config_entry: ConfigEntry,
) -> None:
"""Initialize a Foscam camera.""" """Initialize a Foscam camera."""
super().__init__() super().__init__(coordinator)
Camera.__init__(self)
self._foscam_session = camera self._foscam_session = coordinator.session
self._username = config_entry.data[CONF_USERNAME] self._username = config_entry.data[CONF_USERNAME]
self._password = config_entry.data[CONF_PASSWORD] self._password = config_entry.data[CONF_PASSWORD]
self._stream = config_entry.data[CONF_STREAM] self._stream = config_entry.data[CONF_STREAM]
@ -125,6 +125,9 @@ class HassFoscamCamera(Camera):
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:
"""Handle entity addition to hass.""" """Handle entity addition to hass."""
# Get motion detection status # Get motion detection status
await super().async_added_to_hass()
ret, response = await self.hass.async_add_executor_job( ret, response = await self.hass.async_add_executor_job(
self._foscam_session.get_motion_detect_config self._foscam_session.get_motion_detect_config
) )

View File

@ -0,0 +1,47 @@
"""The foscam coordinator object."""
import asyncio
from datetime import timedelta
from typing import Any
from libpyfoscam import FoscamCamera
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, LOGGER
class FoscamCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Foscam coordinator."""
def __init__(
self,
hass: HomeAssistant,
session: FoscamCamera,
) -> None:
"""Initialize my coordinator."""
super().__init__(
hass,
LOGGER,
name=DOMAIN,
update_interval=timedelta(seconds=30),
)
self.session = session
async def _async_update_data(self) -> dict[str, Any]:
"""Fetch data from API endpoint."""
async with asyncio.timeout(30):
data = {}
ret, dev_info = await self.hass.async_add_executor_job(
self.session.get_dev_info
)
if ret == 0:
data["dev_info"] = dev_info
all_info = await self.hass.async_add_executor_job(
self.session.get_product_all_info
)
data["product_info"] = all_info[1]
return data

View File

@ -1,7 +1,7 @@
{ {
"domain": "foscam", "domain": "foscam",
"name": "Foscam", "name": "Foscam",
"codeowners": ["@skgsergio"], "codeowners": ["@skgsergio", "@krmarien"],
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/foscam", "documentation": "https://www.home-assistant.io/integrations/foscam",
"iot_class": "local_polling", "iot_class": "local_polling",