mirror of
https://github.com/home-assistant/core.git
synced 2025-09-05 21:01:37 +02:00
Add more Foscam switches (#147409)
Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
committed by
GitHub
parent
9e398ffc10
commit
e394435d7c
@@ -30,7 +30,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: FoscamConfigEntry) -> bo
|
|||||||
verbose=False,
|
verbose=False,
|
||||||
)
|
)
|
||||||
coordinator = FoscamCoordinator(hass, entry, session)
|
coordinator = FoscamCoordinator(hass, entry, session)
|
||||||
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
entry.runtime_data = coordinator
|
entry.runtime_data = coordinator
|
||||||
@@ -89,7 +88,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: FoscamConfigEntry) ->
|
|||||||
|
|
||||||
|
|
||||||
async def async_migrate_entities(hass: HomeAssistant, entry: FoscamConfigEntry) -> None:
|
async def async_migrate_entities(hass: HomeAssistant, entry: FoscamConfigEntry) -> None:
|
||||||
"""Migrate old entry."""
|
"""Migrate old entries to support config_entry_id-based unique IDs."""
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _update_unique_id(
|
def _update_unique_id(
|
||||||
|
@@ -11,3 +11,16 @@ CONF_STREAM = "stream"
|
|||||||
|
|
||||||
SERVICE_PTZ = "ptz"
|
SERVICE_PTZ = "ptz"
|
||||||
SERVICE_PTZ_PRESET = "ptz_preset"
|
SERVICE_PTZ_PRESET = "ptz_preset"
|
||||||
|
|
||||||
|
SUPPORTED_SWITCHES = [
|
||||||
|
"flip_switch",
|
||||||
|
"mirror_switch",
|
||||||
|
"ir_switch",
|
||||||
|
"sleep_switch",
|
||||||
|
"white_light_switch",
|
||||||
|
"siren_alarm_switch",
|
||||||
|
"turn_off_volume_switch",
|
||||||
|
"light_status_switch",
|
||||||
|
"hdr_switch",
|
||||||
|
"wdr_switch",
|
||||||
|
]
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
"""The foscam coordinator object."""
|
"""The foscam coordinator object."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from libpyfoscamcgi import FoscamCamera
|
from libpyfoscamcgi import FoscamCamera
|
||||||
|
|
||||||
@@ -15,9 +15,35 @@ from .const import DOMAIN, LOGGER
|
|||||||
type FoscamConfigEntry = ConfigEntry[FoscamCoordinator]
|
type FoscamConfigEntry = ConfigEntry[FoscamCoordinator]
|
||||||
|
|
||||||
|
|
||||||
class FoscamCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
@dataclass
|
||||||
|
class FoscamDeviceInfo:
|
||||||
|
"""A data class representing the current state and configuration of a Foscam camera device."""
|
||||||
|
|
||||||
|
dev_info: dict
|
||||||
|
product_info: dict
|
||||||
|
|
||||||
|
is_open_ir: bool
|
||||||
|
is_flip: bool
|
||||||
|
is_mirror: bool
|
||||||
|
|
||||||
|
is_asleep: dict
|
||||||
|
is_open_white_light: bool
|
||||||
|
is_siren_alarm: bool
|
||||||
|
|
||||||
|
volume: int
|
||||||
|
speak_volume: int
|
||||||
|
is_turn_off_volume: bool
|
||||||
|
is_turn_off_light: bool
|
||||||
|
|
||||||
|
is_open_wdr: bool | None = None
|
||||||
|
is_open_hdr: bool | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class FoscamCoordinator(DataUpdateCoordinator[FoscamDeviceInfo]):
|
||||||
"""Foscam coordinator."""
|
"""Foscam coordinator."""
|
||||||
|
|
||||||
|
config_entry: FoscamConfigEntry
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
@@ -34,24 +60,82 @@ class FoscamCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
|||||||
)
|
)
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
async def _async_update_data(self) -> dict[str, Any]:
|
def gather_all_configs(self) -> FoscamDeviceInfo:
|
||||||
|
"""Get all Foscam configurations."""
|
||||||
|
ret_dev_info, dev_info = self.session.get_dev_info()
|
||||||
|
dev_info = dev_info if ret_dev_info == 0 else {}
|
||||||
|
|
||||||
|
ret_product_info, product_info = self.session.get_product_all_info()
|
||||||
|
product_info = product_info if ret_product_info == 0 else {}
|
||||||
|
|
||||||
|
ret_ir, infra_led_config = self.session.get_infra_led_config()
|
||||||
|
is_open_ir = infra_led_config["mode"] == "1" if ret_ir == 0 else False
|
||||||
|
|
||||||
|
ret_mf, mirror_flip_setting = self.session.get_mirror_and_flip_setting()
|
||||||
|
is_flip = mirror_flip_setting["isFlip"] == "1" if ret_mf == 0 else False
|
||||||
|
is_mirror = mirror_flip_setting["isMirror"] == "1" if ret_mf == 0 else False
|
||||||
|
|
||||||
|
ret_sleep, sleep_setting = self.session.is_asleep()
|
||||||
|
is_asleep = {"supported": ret_sleep == 0, "status": bool(int(sleep_setting))}
|
||||||
|
|
||||||
|
ret_wl, is_open_white_light = self.session.getWhiteLightBrightness()
|
||||||
|
is_open_white_light_val = (
|
||||||
|
is_open_white_light["enable"] == "1" if ret_wl == 0 else False
|
||||||
|
)
|
||||||
|
|
||||||
|
ret_sc, is_siren_alarm = self.session.getSirenConfig()
|
||||||
|
is_siren_alarm_val = (
|
||||||
|
is_siren_alarm["sirenEnable"] == "1" if ret_sc == 0 else False
|
||||||
|
)
|
||||||
|
|
||||||
|
ret_vol, volume = self.session.getAudioVolume()
|
||||||
|
volume_val = int(volume["volume"]) if ret_vol == 0 else 0
|
||||||
|
|
||||||
|
ret_sv, speak_volume = self.session.getSpeakVolume()
|
||||||
|
speak_volume_val = int(speak_volume["SpeakVolume"]) if ret_sv == 0 else 0
|
||||||
|
|
||||||
|
ret_ves, is_turn_off_volume = self.session.getVoiceEnableState()
|
||||||
|
is_turn_off_volume_val = not (
|
||||||
|
ret_ves == 0 and is_turn_off_volume["isEnable"] == "1"
|
||||||
|
)
|
||||||
|
|
||||||
|
ret_les, is_turn_off_light = self.session.getLedEnableState()
|
||||||
|
is_turn_off_light_val = not (
|
||||||
|
ret_les == 0 and is_turn_off_light["isEnable"] == "0"
|
||||||
|
)
|
||||||
|
|
||||||
|
is_open_wdr = None
|
||||||
|
is_open_hdr = None
|
||||||
|
reserve3 = product_info.get("reserve3")
|
||||||
|
reserve3_int = int(reserve3) if reserve3 is not None else 0
|
||||||
|
|
||||||
|
if (reserve3_int & (1 << 8)) != 0:
|
||||||
|
ret_wdr, is_open_wdr_data = self.session.getWdrMode()
|
||||||
|
mode = is_open_wdr_data["mode"] if ret_wdr == 0 and is_open_wdr_data else 0
|
||||||
|
is_open_wdr = bool(int(mode))
|
||||||
|
else:
|
||||||
|
ret_hdr, is_open_hdr_data = self.session.getHdrMode()
|
||||||
|
mode = is_open_hdr_data["mode"] if ret_hdr == 0 and is_open_hdr_data else 0
|
||||||
|
is_open_hdr = bool(int(mode))
|
||||||
|
|
||||||
|
return FoscamDeviceInfo(
|
||||||
|
dev_info=dev_info,
|
||||||
|
product_info=product_info,
|
||||||
|
is_open_ir=is_open_ir,
|
||||||
|
is_flip=is_flip,
|
||||||
|
is_mirror=is_mirror,
|
||||||
|
is_asleep=is_asleep,
|
||||||
|
is_open_white_light=is_open_white_light_val,
|
||||||
|
is_siren_alarm=is_siren_alarm_val,
|
||||||
|
volume=volume_val,
|
||||||
|
speak_volume=speak_volume_val,
|
||||||
|
is_turn_off_volume=is_turn_off_volume_val,
|
||||||
|
is_turn_off_light=is_turn_off_light_val,
|
||||||
|
is_open_wdr=is_open_wdr,
|
||||||
|
is_open_hdr=is_open_hdr,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> FoscamDeviceInfo:
|
||||||
"""Fetch data from API endpoint."""
|
"""Fetch data from API endpoint."""
|
||||||
|
async with asyncio.timeout(10):
|
||||||
async with asyncio.timeout(30):
|
return await self.hass.async_add_executor_job(self.gather_all_configs)
|
||||||
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]
|
|
||||||
|
|
||||||
ret, is_asleep = await self.hass.async_add_executor_job(
|
|
||||||
self.session.is_asleep
|
|
||||||
)
|
|
||||||
data["is_asleep"] = {"supported": ret == 0, "status": is_asleep}
|
|
||||||
return data
|
|
||||||
|
@@ -13,19 +13,15 @@ from .coordinator import FoscamCoordinator
|
|||||||
class FoscamEntity(CoordinatorEntity[FoscamCoordinator]):
|
class FoscamEntity(CoordinatorEntity[FoscamCoordinator]):
|
||||||
"""Base entity for Foscam camera."""
|
"""Base entity for Foscam camera."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, coordinator: FoscamCoordinator, config_entry_id: str) -> None:
|
||||||
self,
|
|
||||||
coordinator: FoscamCoordinator,
|
|
||||||
entry_id: str,
|
|
||||||
) -> None:
|
|
||||||
"""Initialize the base Foscam entity."""
|
"""Initialize the base Foscam entity."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
|
|
||||||
self._attr_device_info = DeviceInfo(
|
self._attr_device_info = DeviceInfo(
|
||||||
identifiers={(DOMAIN, entry_id)},
|
identifiers={(DOMAIN, config_entry_id)},
|
||||||
manufacturer="Foscam",
|
manufacturer="Foscam",
|
||||||
)
|
)
|
||||||
if dev_info := coordinator.data.get("dev_info"):
|
if dev_info := coordinator.data.dev_info:
|
||||||
self._attr_device_info[ATTR_MODEL] = dev_info["productName"]
|
self._attr_device_info[ATTR_MODEL] = dev_info["productName"]
|
||||||
self._attr_device_info[ATTR_SW_VERSION] = dev_info["firmwareVer"]
|
self._attr_device_info[ATTR_SW_VERSION] = dev_info["firmwareVer"]
|
||||||
self._attr_device_info[ATTR_HW_VERSION] = dev_info["hardwareVer"]
|
self._attr_device_info[ATTR_HW_VERSION] = dev_info["hardwareVer"]
|
||||||
|
@@ -6,5 +6,39 @@
|
|||||||
"ptz_preset": {
|
"ptz_preset": {
|
||||||
"service": "mdi:target-variant"
|
"service": "mdi:target-variant"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"entity": {
|
||||||
|
"switch": {
|
||||||
|
"flip_switch": {
|
||||||
|
"default": "mdi:flip-vertical"
|
||||||
|
},
|
||||||
|
"mirror_switch": {
|
||||||
|
"default": "mdi:mirror"
|
||||||
|
},
|
||||||
|
"ir_switch": {
|
||||||
|
"default": "mdi:theme-light-dark"
|
||||||
|
},
|
||||||
|
"sleep_switch": {
|
||||||
|
"default": "mdi:sleep"
|
||||||
|
},
|
||||||
|
"white_light_switch": {
|
||||||
|
"default": "mdi:light-flood-down"
|
||||||
|
},
|
||||||
|
"siren_alarm_switch": {
|
||||||
|
"default": "mdi:alarm-note"
|
||||||
|
},
|
||||||
|
"turn_off_volume_switch": {
|
||||||
|
"default": "mdi:volume-off"
|
||||||
|
},
|
||||||
|
"turn_off_light_switch": {
|
||||||
|
"default": "mdi:lightbulb-fluorescent-tube"
|
||||||
|
},
|
||||||
|
"hdr_switch": {
|
||||||
|
"default": "mdi:hdr"
|
||||||
|
},
|
||||||
|
"wdr_switch": {
|
||||||
|
"default": "mdi:alpha-w-box"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,35 @@
|
|||||||
},
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"switch": {
|
"switch": {
|
||||||
|
"flip_switch": {
|
||||||
|
"name": "Flip"
|
||||||
|
},
|
||||||
|
"mirror_switch": {
|
||||||
|
"name": "Mirror"
|
||||||
|
},
|
||||||
|
"ir_switch": {
|
||||||
|
"name": "Infrared mode"
|
||||||
|
},
|
||||||
"sleep_switch": {
|
"sleep_switch": {
|
||||||
"name": "Sleep"
|
"name": "Sleep mode"
|
||||||
|
},
|
||||||
|
"white_light_switch": {
|
||||||
|
"name": "White light"
|
||||||
|
},
|
||||||
|
"siren_alarm_switch": {
|
||||||
|
"name": "Siren alarm"
|
||||||
|
},
|
||||||
|
"turn_off_volume_switch": {
|
||||||
|
"name": "Volume muted"
|
||||||
|
},
|
||||||
|
"turn_off_light_switch": {
|
||||||
|
"name": "Light"
|
||||||
|
},
|
||||||
|
"hdr_switch": {
|
||||||
|
"name": "HDR"
|
||||||
|
},
|
||||||
|
"wdr_switch": {
|
||||||
|
"name": "WDR"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -2,18 +2,117 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from libpyfoscamcgi import FoscamCamera
|
||||||
from homeassistant.core import HomeAssistant, callback
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||||
|
|
||||||
from .const import LOGGER
|
|
||||||
from .coordinator import FoscamConfigEntry, FoscamCoordinator
|
from .coordinator import FoscamConfigEntry, FoscamCoordinator
|
||||||
from .entity import FoscamEntity
|
from .entity import FoscamEntity
|
||||||
|
|
||||||
|
|
||||||
|
def handle_ir_turn_on(session: FoscamCamera) -> None:
|
||||||
|
"""Turn on IR LED: sets IR mode to auto (if supported), then turns off the IR LED."""
|
||||||
|
|
||||||
|
session.set_infra_led_config(1)
|
||||||
|
session.open_infra_led()
|
||||||
|
|
||||||
|
|
||||||
|
def handle_ir_turn_off(session: FoscamCamera) -> None:
|
||||||
|
"""Turn off IR LED: sets IR mode to manual (if supported), then turns open the IR LED."""
|
||||||
|
|
||||||
|
session.set_infra_led_config(0)
|
||||||
|
session.close_infra_led()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class FoscamSwitchEntityDescription(SwitchEntityDescription):
|
||||||
|
"""A custom entity description that supports a turn_off function."""
|
||||||
|
|
||||||
|
native_value_fn: Callable[..., bool]
|
||||||
|
turn_off_fn: Callable[[FoscamCamera], None]
|
||||||
|
turn_on_fn: Callable[[FoscamCamera], None]
|
||||||
|
|
||||||
|
|
||||||
|
SWITCH_DESCRIPTIONS: list[FoscamSwitchEntityDescription] = [
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_flip",
|
||||||
|
translation_key="flip_switch",
|
||||||
|
native_value_fn=lambda data: data.is_flip,
|
||||||
|
turn_off_fn=lambda session: session.flip_video(0),
|
||||||
|
turn_on_fn=lambda session: session.flip_video(1),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_mirror",
|
||||||
|
translation_key="mirror_switch",
|
||||||
|
native_value_fn=lambda data: data.is_mirror,
|
||||||
|
turn_off_fn=lambda session: session.mirror_video(0),
|
||||||
|
turn_on_fn=lambda session: session.mirror_video(1),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_open_ir",
|
||||||
|
translation_key="ir_switch",
|
||||||
|
native_value_fn=lambda data: data.is_open_ir,
|
||||||
|
turn_off_fn=handle_ir_turn_off,
|
||||||
|
turn_on_fn=handle_ir_turn_on,
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="sleep_switch",
|
||||||
|
translation_key="sleep_switch",
|
||||||
|
native_value_fn=lambda data: data.is_asleep["status"],
|
||||||
|
turn_off_fn=lambda session: session.wake_up(),
|
||||||
|
turn_on_fn=lambda session: session.sleep(),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_open_white_light",
|
||||||
|
translation_key="white_light_switch",
|
||||||
|
native_value_fn=lambda data: data.is_open_white_light,
|
||||||
|
turn_off_fn=lambda session: session.closeWhiteLight(),
|
||||||
|
turn_on_fn=lambda session: session.openWhiteLight(),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_siren_alarm",
|
||||||
|
translation_key="siren_alarm_switch",
|
||||||
|
native_value_fn=lambda data: data.is_siren_alarm,
|
||||||
|
turn_off_fn=lambda session: session.setSirenConfig(0, 100, 0),
|
||||||
|
turn_on_fn=lambda session: session.setSirenConfig(1, 100, 0),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_turn_off_volume",
|
||||||
|
translation_key="turn_off_volume_switch",
|
||||||
|
native_value_fn=lambda data: data.is_turn_off_volume,
|
||||||
|
turn_off_fn=lambda session: session.setVoiceEnableState(1),
|
||||||
|
turn_on_fn=lambda session: session.setVoiceEnableState(0),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_turn_off_light",
|
||||||
|
translation_key="turn_off_light_switch",
|
||||||
|
native_value_fn=lambda data: data.is_turn_off_light,
|
||||||
|
turn_off_fn=lambda session: session.setLedEnableState(0),
|
||||||
|
turn_on_fn=lambda session: session.setLedEnableState(1),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_open_hdr",
|
||||||
|
translation_key="hdr_switch",
|
||||||
|
native_value_fn=lambda data: data.is_open_hdr,
|
||||||
|
turn_off_fn=lambda session: session.setHdrMode(0),
|
||||||
|
turn_on_fn=lambda session: session.setHdrMode(1),
|
||||||
|
),
|
||||||
|
FoscamSwitchEntityDescription(
|
||||||
|
key="is_open_wdr",
|
||||||
|
translation_key="wdr_switch",
|
||||||
|
native_value_fn=lambda data: data.is_open_wdr,
|
||||||
|
turn_off_fn=lambda session: session.setWdrMode(0),
|
||||||
|
turn_on_fn=lambda session: session.setWdrMode(1),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry: FoscamConfigEntry,
|
config_entry: FoscamConfigEntry,
|
||||||
@@ -22,63 +121,61 @@ async def async_setup_entry(
|
|||||||
"""Set up foscam switch from a config entry."""
|
"""Set up foscam switch from a config entry."""
|
||||||
|
|
||||||
coordinator = config_entry.runtime_data
|
coordinator = config_entry.runtime_data
|
||||||
|
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
if coordinator.data["is_asleep"]["supported"]:
|
entities = []
|
||||||
async_add_entities([FoscamSleepSwitch(coordinator, config_entry)])
|
|
||||||
|
product_info = coordinator.data.product_info
|
||||||
|
reserve3 = product_info.get("reserve3", "0")
|
||||||
|
|
||||||
|
for description in SWITCH_DESCRIPTIONS:
|
||||||
|
if description.key == "is_asleep":
|
||||||
|
if not coordinator.data.is_asleep["supported"]:
|
||||||
|
continue
|
||||||
|
elif description.key == "is_open_hdr":
|
||||||
|
if ((1 << 8) & int(reserve3)) != 0 or ((1 << 7) & int(reserve3)) == 0:
|
||||||
|
continue
|
||||||
|
elif description.key == "is_open_wdr":
|
||||||
|
if ((1 << 8) & int(reserve3)) == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
entities.append(FoscamGenericSwitch(coordinator, description))
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
class FoscamSleepSwitch(FoscamEntity, SwitchEntity):
|
class FoscamGenericSwitch(FoscamEntity, SwitchEntity):
|
||||||
"""An implementation for Sleep Switch."""
|
"""A generic switch class for Foscam entities."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
entity_description: FoscamSwitchEntityDescription
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
coordinator: FoscamCoordinator,
|
coordinator: FoscamCoordinator,
|
||||||
config_entry: FoscamConfigEntry,
|
description: FoscamSwitchEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize a Foscam Sleep Switch."""
|
"""Initialize the generic switch."""
|
||||||
super().__init__(coordinator, config_entry.entry_id)
|
entry_id = coordinator.config_entry.entry_id
|
||||||
|
super().__init__(coordinator, entry_id)
|
||||||
|
|
||||||
self._attr_unique_id = f"{config_entry.entry_id}_sleep_switch"
|
self.entity_description = description
|
||||||
self._attr_translation_key = "sleep_switch"
|
self._attr_unique_id = f"{entry_id}_{description.key}"
|
||||||
self._attr_has_entity_name = True
|
|
||||||
|
|
||||||
self.is_asleep = self.coordinator.data["is_asleep"]["status"]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self) -> bool:
|
||||||
"""Return true if camera is asleep."""
|
"""Return the state of the switch."""
|
||||||
return self.is_asleep
|
return self.entity_description.native_value_fn(self.coordinator.data)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Wake camera."""
|
"""Turn off the entity."""
|
||||||
LOGGER.debug("Wake camera")
|
self.hass.async_add_executor_job(
|
||||||
|
self.entity_description.turn_off_fn, self.coordinator.session
|
||||||
ret, _ = await self.hass.async_add_executor_job(
|
|
||||||
self.coordinator.session.wake_up
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if ret != 0:
|
|
||||||
raise HomeAssistantError(f"Error waking up: {ret}")
|
|
||||||
|
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""But camera is sleep."""
|
"""Turn on the entity."""
|
||||||
LOGGER.debug("Sleep camera")
|
self.hass.async_add_executor_job(
|
||||||
|
self.entity_description.turn_on_fn, self.coordinator.session
|
||||||
ret, _ = await self.hass.async_add_executor_job(self.coordinator.session.sleep)
|
)
|
||||||
|
|
||||||
if ret != 0:
|
|
||||||
raise HomeAssistantError(f"Error sleeping: {ret}")
|
|
||||||
|
|
||||||
await self.coordinator.async_request_refresh()
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
@callback
|
|
||||||
def _handle_coordinator_update(self) -> None:
|
|
||||||
"""Handle updated data from the coordinator."""
|
|
||||||
|
|
||||||
self.is_asleep = self.coordinator.data["is_asleep"]["status"]
|
|
||||||
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
@@ -60,6 +60,21 @@ def setup_mock_foscam_camera(mock_foscam_camera):
|
|||||||
mock_foscam_camera.get_dev_info.return_value = (dev_info_rc, dev_info_data)
|
mock_foscam_camera.get_dev_info.return_value = (dev_info_rc, dev_info_data)
|
||||||
mock_foscam_camera.get_port_info.return_value = (dev_info_rc, {})
|
mock_foscam_camera.get_port_info.return_value = (dev_info_rc, {})
|
||||||
mock_foscam_camera.is_asleep.return_value = (0, True)
|
mock_foscam_camera.is_asleep.return_value = (0, True)
|
||||||
|
mock_foscam_camera.get_infra_led_config.return_value = (0, {"mode": "1"})
|
||||||
|
mock_foscam_camera.get_mirror_and_flip_setting.return_value = (
|
||||||
|
0,
|
||||||
|
{"isFlip": "0", "isMirror": "0"},
|
||||||
|
)
|
||||||
|
mock_foscam_camera.is_asleep.return_value = (0, "0")
|
||||||
|
mock_foscam_camera.getWhiteLightBrightness.return_value = (0, {"enable": "1"})
|
||||||
|
mock_foscam_camera.getSirenConfig.return_value = (0, {"sirenEnable": "1"})
|
||||||
|
mock_foscam_camera.getAudioVolume.return_value = (0, {"volume": "100"})
|
||||||
|
mock_foscam_camera.getSpeakVolume.return_value = (0, {"SpeakVolume": "100"})
|
||||||
|
mock_foscam_camera.getVoiceEnableState.return_value = (0, {"isEnable": "1"})
|
||||||
|
mock_foscam_camera.getLedEnableState.return_value = (0, {"isEnable": "0"})
|
||||||
|
mock_foscam_camera.getWdrMode.return_value = (0, {"mode": "0"})
|
||||||
|
mock_foscam_camera.getHdrMode.return_value = (0, {"mode": "0"})
|
||||||
|
mock_foscam_camera.get_motion_detect_config.return_value = (0, 1)
|
||||||
|
|
||||||
return mock_foscam_camera
|
return mock_foscam_camera
|
||||||
|
|
||||||
|
385
tests/components/foscam/snapshots/test_switch.ambr
Normal file
385
tests/components/foscam/snapshots/test_switch.ambr
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_entities[switch.mock_title_flip-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_flip',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Flip',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'flip_switch',
|
||||||
|
'unique_id': '123ABC_is_flip',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_flip-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Flip',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_flip',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_infrared_mode-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_infrared_mode',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Infrared mode',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'ir_switch',
|
||||||
|
'unique_id': '123ABC_is_open_ir',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_infrared_mode-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Infrared mode',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_infrared_mode',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_light-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_light',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Light',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'turn_off_light_switch',
|
||||||
|
'unique_id': '123ABC_is_turn_off_light',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_light-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Light',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_light',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_mirror-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_mirror',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Mirror',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'mirror_switch',
|
||||||
|
'unique_id': '123ABC_is_mirror',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_mirror-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Mirror',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_mirror',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_siren_alarm-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_siren_alarm',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Siren alarm',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'siren_alarm_switch',
|
||||||
|
'unique_id': '123ABC_is_siren_alarm',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_siren_alarm-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Siren alarm',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_siren_alarm',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_sleep_mode-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_sleep_mode',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Sleep mode',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'sleep_switch',
|
||||||
|
'unique_id': '123ABC_sleep_switch',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_sleep_mode-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Sleep mode',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_sleep_mode',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_volume_muted-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_volume_muted',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Volume muted',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'turn_off_volume_switch',
|
||||||
|
'unique_id': '123ABC_is_turn_off_volume',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_volume_muted-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title Volume muted',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_volume_muted',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_white_light-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'config_subentry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.mock_title_white_light',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'White light',
|
||||||
|
'platform': 'foscam',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'suggested_object_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'white_light_switch',
|
||||||
|
'unique_id': '123ABC_is_open_white_light',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[switch.mock_title_white_light-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'Mock Title White light',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.mock_title_white_light',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
@@ -96,7 +96,7 @@ async def test_unique_id_migration_not_needed(
|
|||||||
assert entity_before.unique_id == f"{ENTRY_ID}_sleep_switch"
|
assert entity_before.unique_id == f"{ENTRY_ID}_sleep_switch"
|
||||||
|
|
||||||
with (
|
with (
|
||||||
# Mock a valid camera instance"
|
# Mock a valid camera instance
|
||||||
patch("homeassistant.components.foscam.FoscamCamera") as mock_foscam_camera,
|
patch("homeassistant.components.foscam.FoscamCamera") as mock_foscam_camera,
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.foscam.async_migrate_entry",
|
"homeassistant.components.foscam.async_migrate_entry",
|
||||||
|
35
tests/components/foscam/test_switch.py
Normal file
35
tests/components/foscam/test_switch.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""Test for the switch platform entity of the foscam component."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.foscam.const import DOMAIN
|
||||||
|
from homeassistant.const import Platform
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from .conftest import setup_mock_foscam_camera
|
||||||
|
from .const import ENTRY_ID, VALID_CONFIG
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entities(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test that coordinator returns the data we expect after the first refresh."""
|
||||||
|
entry = MockConfigEntry(domain=DOMAIN, data=VALID_CONFIG, entry_id=ENTRY_ID)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
with (
|
||||||
|
# Mock a valid camera instance"
|
||||||
|
patch("homeassistant.components.foscam.FoscamCamera") as mock_foscam_camera,
|
||||||
|
patch("homeassistant.components.foscam.PLATFORMS", [Platform.SWITCH]),
|
||||||
|
):
|
||||||
|
setup_mock_foscam_camera(mock_foscam_camera)
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
|
||||||
|
await snapshot_platform(hass, entity_registry, snapshot, entry.entry_id)
|
Reference in New Issue
Block a user