diff --git a/.coveragerc b/.coveragerc index 83707b6b9f1..fe2ba33b0aa 100644 --- a/.coveragerc +++ b/.coveragerc @@ -421,6 +421,7 @@ omit = homeassistant/components/fortios/device_tracker.py homeassistant/components/foscam/__init__.py homeassistant/components/foscam/camera.py + homeassistant/components/foscam/coordinator.py homeassistant/components/foursquare/* homeassistant/components/free_mobile/notify.py homeassistant/components/freebox/camera.py diff --git a/CODEOWNERS b/CODEOWNERS index 052d3b9258e..c5ac30ea6df 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -418,8 +418,8 @@ build.json @home-assistant/supervisor /homeassistant/components/forked_daapd/ @uvjustin /tests/components/forked_daapd/ @uvjustin /homeassistant/components/fortios/ @kimfrellsen -/homeassistant/components/foscam/ @skgsergio -/tests/components/foscam/ @skgsergio +/homeassistant/components/foscam/ @skgsergio @krmarien +/tests/components/foscam/ @skgsergio @krmarien /homeassistant/components/freebox/ @hacf-fr @Quentame /tests/components/freebox/ @hacf-fr @Quentame /homeassistant/components/freedompro/ @stefano055415 diff --git a/homeassistant/components/foscam/__init__.py b/homeassistant/components/foscam/__init__.py index ef88d0f671a..057ef4dbe8c 100644 --- a/homeassistant/components/foscam/__init__.py +++ b/homeassistant/components/foscam/__init__.py @@ -15,15 +15,28 @@ from homeassistant.helpers.entity_registry import async_migrate_entries from .config_flow import DEFAULT_RTSP_PORT from .const import CONF_RTSP_PORT, DOMAIN, LOGGER, SERVICE_PTZ, SERVICE_PTZ_PRESET +from .coordinator import FoscamCoordinator PLATFORMS = [Platform.CAMERA] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """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 diff --git a/homeassistant/components/foscam/camera.py b/homeassistant/components/foscam/camera.py index 384aea4c5fa..c07ddfd9bfb 100644 --- a/homeassistant/components/foscam/camera.py +++ b/homeassistant/components/foscam/camera.py @@ -3,16 +3,16 @@ from __future__ import annotations import asyncio -from libpyfoscam import FoscamCamera import voluptuous as vol from homeassistant.components.camera import Camera, CameraEntityFeature 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.helpers import config_validation as cv, entity_platform from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import ( CONF_RTSP_PORT, @@ -22,6 +22,7 @@ from .const import ( SERVICE_PTZ, SERVICE_PTZ_PRESET, ) +from .coordinator import FoscamCoordinator DIR_UP = "up" DIR_DOWN = "down" @@ -88,28 +89,27 @@ async def async_setup_entry( "async_perform_ptz_preset", ) - camera = FoscamCamera( - config_entry.data[CONF_HOST], - config_entry.data[CONF_PORT], - config_entry.data[CONF_USERNAME], - config_entry.data[CONF_PASSWORD], - verbose=False, - ) + coordinator: FoscamCoordinator = hass.data[DOMAIN][config_entry.entry_id] - 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.""" _attr_has_entity_name = True _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.""" - 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._password = config_entry.data[CONF_PASSWORD] self._stream = config_entry.data[CONF_STREAM] @@ -125,6 +125,9 @@ class HassFoscamCamera(Camera): async def async_added_to_hass(self) -> None: """Handle entity addition to hass.""" # Get motion detection status + + await super().async_added_to_hass() + ret, response = await self.hass.async_add_executor_job( self._foscam_session.get_motion_detect_config ) diff --git a/homeassistant/components/foscam/coordinator.py b/homeassistant/components/foscam/coordinator.py new file mode 100644 index 00000000000..063d5235c04 --- /dev/null +++ b/homeassistant/components/foscam/coordinator.py @@ -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 diff --git a/homeassistant/components/foscam/manifest.json b/homeassistant/components/foscam/manifest.json index fc7cbb72e3c..da4e9f53af4 100644 --- a/homeassistant/components/foscam/manifest.json +++ b/homeassistant/components/foscam/manifest.json @@ -1,7 +1,7 @@ { "domain": "foscam", "name": "Foscam", - "codeowners": ["@skgsergio"], + "codeowners": ["@skgsergio", "@krmarien"], "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/foscam", "iot_class": "local_polling",