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,
|
||||
)
|
||||
coordinator = FoscamCoordinator(hass, entry, session)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
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:
|
||||
"""Migrate old entry."""
|
||||
"""Migrate old entries to support config_entry_id-based unique IDs."""
|
||||
|
||||
@callback
|
||||
def _update_unique_id(
|
||||
|
@@ -11,3 +11,16 @@ CONF_STREAM = "stream"
|
||||
|
||||
SERVICE_PTZ = "ptz"
|
||||
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."""
|
||||
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from libpyfoscamcgi import FoscamCamera
|
||||
|
||||
@@ -15,9 +15,35 @@ from .const import DOMAIN, LOGGER
|
||||
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."""
|
||||
|
||||
config_entry: FoscamConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
@@ -34,24 +60,82 @@ class FoscamCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
)
|
||||
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."""
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
async with asyncio.timeout(10):
|
||||
return await self.hass.async_add_executor_job(self.gather_all_configs)
|
||||
|
@@ -13,19 +13,15 @@ from .coordinator import FoscamCoordinator
|
||||
class FoscamEntity(CoordinatorEntity[FoscamCoordinator]):
|
||||
"""Base entity for Foscam camera."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: FoscamCoordinator,
|
||||
entry_id: str,
|
||||
) -> None:
|
||||
def __init__(self, coordinator: FoscamCoordinator, config_entry_id: str) -> None:
|
||||
"""Initialize the base Foscam entity."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, entry_id)},
|
||||
identifiers={(DOMAIN, config_entry_id)},
|
||||
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_SW_VERSION] = dev_info["firmwareVer"]
|
||||
self._attr_device_info[ATTR_HW_VERSION] = dev_info["hardwareVer"]
|
||||
|
@@ -6,5 +6,39 @@
|
||||
"ptz_preset": {
|
||||
"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": {
|
||||
"switch": {
|
||||
"flip_switch": {
|
||||
"name": "Flip"
|
||||
},
|
||||
"mirror_switch": {
|
||||
"name": "Mirror"
|
||||
},
|
||||
"ir_switch": {
|
||||
"name": "Infrared mode"
|
||||
},
|
||||
"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 collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from libpyfoscamcgi import FoscamCamera
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import LOGGER
|
||||
from .coordinator import FoscamConfigEntry, FoscamCoordinator
|
||||
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(
|
||||
hass: HomeAssistant,
|
||||
config_entry: FoscamConfigEntry,
|
||||
@@ -22,63 +121,61 @@ async def async_setup_entry(
|
||||
"""Set up foscam switch from a config entry."""
|
||||
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
if coordinator.data["is_asleep"]["supported"]:
|
||||
async_add_entities([FoscamSleepSwitch(coordinator, config_entry)])
|
||||
entities = []
|
||||
|
||||
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):
|
||||
"""An implementation for Sleep Switch."""
|
||||
class FoscamGenericSwitch(FoscamEntity, SwitchEntity):
|
||||
"""A generic switch class for Foscam entities."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
entity_description: FoscamSwitchEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: FoscamCoordinator,
|
||||
config_entry: FoscamConfigEntry,
|
||||
description: FoscamSwitchEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize a Foscam Sleep Switch."""
|
||||
super().__init__(coordinator, config_entry.entry_id)
|
||||
"""Initialize the generic switch."""
|
||||
entry_id = coordinator.config_entry.entry_id
|
||||
super().__init__(coordinator, entry_id)
|
||||
|
||||
self._attr_unique_id = f"{config_entry.entry_id}_sleep_switch"
|
||||
self._attr_translation_key = "sleep_switch"
|
||||
self._attr_has_entity_name = True
|
||||
|
||||
self.is_asleep = self.coordinator.data["is_asleep"]["status"]
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{entry_id}_{description.key}"
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if camera is asleep."""
|
||||
return self.is_asleep
|
||||
def is_on(self) -> bool:
|
||||
"""Return the state of the switch."""
|
||||
return self.entity_description.native_value_fn(self.coordinator.data)
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Wake camera."""
|
||||
LOGGER.debug("Wake camera")
|
||||
|
||||
ret, _ = await self.hass.async_add_executor_job(
|
||||
self.coordinator.session.wake_up
|
||||
"""Turn off the entity."""
|
||||
self.hass.async_add_executor_job(
|
||||
self.entity_description.turn_off_fn, self.coordinator.session
|
||||
)
|
||||
|
||||
if ret != 0:
|
||||
raise HomeAssistantError(f"Error waking up: {ret}")
|
||||
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""But camera is sleep."""
|
||||
LOGGER.debug("Sleep camera")
|
||||
|
||||
ret, _ = await self.hass.async_add_executor_job(self.coordinator.session.sleep)
|
||||
|
||||
if ret != 0:
|
||||
raise HomeAssistantError(f"Error sleeping: {ret}")
|
||||
|
||||
"""Turn on the entity."""
|
||||
self.hass.async_add_executor_job(
|
||||
self.entity_description.turn_on_fn, self.coordinator.session
|
||||
)
|
||||
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_port_info.return_value = (dev_info_rc, {})
|
||||
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
|
||||
|
||||
|
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"
|
||||
|
||||
with (
|
||||
# Mock a valid camera instance"
|
||||
# Mock a valid camera instance
|
||||
patch("homeassistant.components.foscam.FoscamCamera") as mock_foscam_camera,
|
||||
patch(
|
||||
"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