mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
New Foscam switch (#152732)
This commit is contained in:
committed by
GitHub
parent
eab1205823
commit
2d0b4dd7e9
@@ -35,9 +35,16 @@ class FoscamDeviceInfo:
|
||||
is_turn_off_volume: bool
|
||||
is_turn_off_light: bool
|
||||
supports_speak_volume_adjustment: bool
|
||||
supports_pet_adjustment: bool
|
||||
supports_car_adjustment: bool
|
||||
supports_wdr_adjustment: bool
|
||||
supports_hdr_adjustment: bool
|
||||
|
||||
is_open_wdr: bool | None = None
|
||||
is_open_hdr: bool | None = None
|
||||
is_pet_detection_on: bool | None = None
|
||||
is_car_detection_on: bool | None = None
|
||||
is_human_detection_on: bool | None = None
|
||||
|
||||
|
||||
class FoscamCoordinator(DataUpdateCoordinator[FoscamDeviceInfo]):
|
||||
@@ -107,14 +114,15 @@ class FoscamCoordinator(DataUpdateCoordinator[FoscamDeviceInfo]):
|
||||
|
||||
is_open_wdr = None
|
||||
is_open_hdr = None
|
||||
reserve3 = product_info.get("reserve3")
|
||||
reserve3 = product_info.get("reserve4")
|
||||
reserve3_int = int(reserve3) if reserve3 is not None else 0
|
||||
|
||||
if (reserve3_int & (1 << 8)) != 0:
|
||||
supports_wdr_adjustment_val = bool(int(reserve3_int & 256))
|
||||
supports_hdr_adjustment_val = bool(int(reserve3_int & 128))
|
||||
if supports_wdr_adjustment_val:
|
||||
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:
|
||||
elif supports_hdr_adjustment_val:
|
||||
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))
|
||||
@@ -126,6 +134,34 @@ class FoscamCoordinator(DataUpdateCoordinator[FoscamDeviceInfo]):
|
||||
if ret_sw == 0
|
||||
else False
|
||||
)
|
||||
pet_adjustment_val = (
|
||||
bool(int(software_capabilities.get("swCapabilities2")) & 512)
|
||||
if ret_sw == 0
|
||||
else False
|
||||
)
|
||||
car_adjustment_val = (
|
||||
bool(int(software_capabilities.get("swCapabilities2")) & 256)
|
||||
if ret_sw == 0
|
||||
else False
|
||||
)
|
||||
ret_md, mothion_config_val = self.session.get_motion_detect_config()
|
||||
if pet_adjustment_val:
|
||||
is_pet_detection_on_val = (
|
||||
mothion_config_val["petEnable"] == "1" if ret_md == 0 else False
|
||||
)
|
||||
else:
|
||||
is_pet_detection_on_val = False
|
||||
|
||||
if car_adjustment_val:
|
||||
is_car_detection_on_val = (
|
||||
mothion_config_val["carEnable"] == "1" if ret_md == 0 else False
|
||||
)
|
||||
else:
|
||||
is_car_detection_on_val = False
|
||||
|
||||
is_human_detection_on_val = (
|
||||
mothion_config_val["humanEnable"] == "1" if ret_md == 0 else False
|
||||
)
|
||||
|
||||
return FoscamDeviceInfo(
|
||||
dev_info=dev_info,
|
||||
@@ -141,8 +177,15 @@ class FoscamCoordinator(DataUpdateCoordinator[FoscamDeviceInfo]):
|
||||
is_turn_off_volume=is_turn_off_volume_val,
|
||||
is_turn_off_light=is_turn_off_light_val,
|
||||
supports_speak_volume_adjustment=supports_speak_volume_adjustment_val,
|
||||
supports_pet_adjustment=pet_adjustment_val,
|
||||
supports_car_adjustment=car_adjustment_val,
|
||||
supports_hdr_adjustment=supports_hdr_adjustment_val,
|
||||
supports_wdr_adjustment=supports_wdr_adjustment_val,
|
||||
is_open_wdr=is_open_wdr,
|
||||
is_open_hdr=is_open_hdr,
|
||||
is_pet_detection_on=is_pet_detection_on_val,
|
||||
is_car_detection_on=is_car_detection_on_val,
|
||||
is_human_detection_on=is_human_detection_on_val,
|
||||
)
|
||||
|
||||
async def _async_update_data(self) -> FoscamDeviceInfo:
|
||||
|
||||
@@ -38,6 +38,15 @@
|
||||
},
|
||||
"wdr_switch": {
|
||||
"default": "mdi:alpha-w-box"
|
||||
},
|
||||
"pet_detection": {
|
||||
"default": "mdi:paw"
|
||||
},
|
||||
"car_detection": {
|
||||
"default": "mdi:car-hatchback"
|
||||
},
|
||||
"human_detection": {
|
||||
"default": "mdi:human"
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
|
||||
@@ -22,7 +22,7 @@ class FoscamNumberEntityDescription(NumberEntityDescription):
|
||||
|
||||
native_value_fn: Callable[[FoscamCoordinator], int]
|
||||
set_value_fn: Callable[[FoscamCamera, float], Any]
|
||||
exists_fn: Callable[[FoscamCoordinator], bool]
|
||||
exists_fn: Callable[[FoscamCoordinator], bool] = lambda _: True
|
||||
|
||||
|
||||
NUMBER_DESCRIPTIONS: list[FoscamNumberEntityDescription] = [
|
||||
@@ -34,7 +34,6 @@ NUMBER_DESCRIPTIONS: list[FoscamNumberEntityDescription] = [
|
||||
native_step=1,
|
||||
native_value_fn=lambda coordinator: coordinator.data.device_volume,
|
||||
set_value_fn=lambda session, value: session.setAudioVolume(value),
|
||||
exists_fn=lambda _: True,
|
||||
),
|
||||
FoscamNumberEntityDescription(
|
||||
key="speak_volume",
|
||||
|
||||
@@ -61,6 +61,15 @@
|
||||
},
|
||||
"wdr_switch": {
|
||||
"name": "WDR"
|
||||
},
|
||||
"pet_detection": {
|
||||
"name": "Pet detection"
|
||||
},
|
||||
"car_detection": {
|
||||
"name": "Car detection"
|
||||
},
|
||||
"human_detection": {
|
||||
"name": "Human detection"
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
|
||||
@@ -30,6 +30,14 @@ def handle_ir_turn_off(session: FoscamCamera) -> None:
|
||||
session.close_infra_led()
|
||||
|
||||
|
||||
def set_motion_detection(session: FoscamCamera, field: str, enabled: bool) -> None:
|
||||
"""Turns on pet detection."""
|
||||
ret, config = session.get_motion_detect_config()
|
||||
if not ret:
|
||||
config[field] = int(enabled)
|
||||
session.set_motion_detect_config(config)
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class FoscamSwitchEntityDescription(SwitchEntityDescription):
|
||||
"""A custom entity description that supports a turn_off function."""
|
||||
@@ -37,6 +45,7 @@ class FoscamSwitchEntityDescription(SwitchEntityDescription):
|
||||
native_value_fn: Callable[..., bool]
|
||||
turn_off_fn: Callable[[FoscamCamera], None]
|
||||
turn_on_fn: Callable[[FoscamCamera], None]
|
||||
exists_fn: Callable[[FoscamCoordinator], bool] = lambda _: True
|
||||
|
||||
|
||||
SWITCH_DESCRIPTIONS: list[FoscamSwitchEntityDescription] = [
|
||||
@@ -102,6 +111,7 @@ SWITCH_DESCRIPTIONS: list[FoscamSwitchEntityDescription] = [
|
||||
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),
|
||||
exists_fn=lambda coordinator: coordinator.data.supports_hdr_adjustment,
|
||||
),
|
||||
FoscamSwitchEntityDescription(
|
||||
key="is_open_wdr",
|
||||
@@ -109,6 +119,30 @@ SWITCH_DESCRIPTIONS: list[FoscamSwitchEntityDescription] = [
|
||||
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),
|
||||
exists_fn=lambda coordinator: coordinator.data.supports_wdr_adjustment,
|
||||
),
|
||||
FoscamSwitchEntityDescription(
|
||||
key="pet_detection",
|
||||
translation_key="pet_detection",
|
||||
native_value_fn=lambda data: data.is_pet_detection_on,
|
||||
turn_off_fn=lambda session: set_motion_detection(session, "petEnable", False),
|
||||
turn_on_fn=lambda session: set_motion_detection(session, "petEnable", True),
|
||||
exists_fn=lambda coordinator: coordinator.data.supports_pet_adjustment,
|
||||
),
|
||||
FoscamSwitchEntityDescription(
|
||||
key="car_detection",
|
||||
translation_key="car_detection",
|
||||
native_value_fn=lambda data: data.is_car_detection_on,
|
||||
turn_off_fn=lambda session: set_motion_detection(session, "carEnable", False),
|
||||
turn_on_fn=lambda session: set_motion_detection(session, "carEnable", True),
|
||||
exists_fn=lambda coordinator: coordinator.data.supports_car_adjustment,
|
||||
),
|
||||
FoscamSwitchEntityDescription(
|
||||
key="human_detection",
|
||||
translation_key="human_detection",
|
||||
native_value_fn=lambda data: data.is_human_detection_on,
|
||||
turn_off_fn=lambda session: set_motion_detection(session, "humanEnable", False),
|
||||
turn_on_fn=lambda session: set_motion_detection(session, "humanEnable", True),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -122,24 +156,11 @@ async def async_setup_entry(
|
||||
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
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)
|
||||
async_add_entities(
|
||||
FoscamGenericSwitch(coordinator, description)
|
||||
for description in SWITCH_DESCRIPTIONS
|
||||
if description.exists_fn(coordinator)
|
||||
)
|
||||
|
||||
|
||||
class FoscamGenericSwitch(FoscamEntity, SwitchEntity):
|
||||
|
||||
@@ -79,11 +79,16 @@ def setup_mock_foscam_camera(mock_foscam_camera):
|
||||
0,
|
||||
{
|
||||
"swCapabilities1": "100",
|
||||
"swCapbilities2": "100",
|
||||
"swCapbilities3": "100",
|
||||
"swCapbilities4": "100",
|
||||
"swCapabilities2": "768",
|
||||
"swCapabilities3": "100",
|
||||
"swCapabilities4": "100",
|
||||
},
|
||||
)
|
||||
mock_foscam_camera.get_motion_detect_config.return_value = (
|
||||
0,
|
||||
{"petEnable": "1", "carEnable": "1", "humanEnable": "1"},
|
||||
)
|
||||
|
||||
return mock_foscam_camera
|
||||
|
||||
mock_foscam_camera.side_effect = configure_mock_on_init
|
||||
|
||||
@@ -1,4 +1,52 @@
|
||||
# serializer version: 1
|
||||
# name: test_entities[switch.mock_title_car_detection-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_car_detection',
|
||||
'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': 'Car detection',
|
||||
'platform': 'foscam',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'car_detection',
|
||||
'unique_id': '123ABC_car_detection',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_car_detection-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Title Car detection',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.mock_title_car_detection',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_flip-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -47,6 +95,54 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_human_detection-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_human_detection',
|
||||
'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': 'Human detection',
|
||||
'platform': 'foscam',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'human_detection',
|
||||
'unique_id': '123ABC_human_detection',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_human_detection-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Title Human detection',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.mock_title_human_detection',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_infrared_mode-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
@@ -191,6 +287,54 @@
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_pet_detection-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_pet_detection',
|
||||
'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': 'Pet detection',
|
||||
'platform': 'foscam',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'pet_detection',
|
||||
'unique_id': '123ABC_pet_detection',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_pet_detection-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Mock Title Pet detection',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.mock_title_pet_detection',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'on',
|
||||
})
|
||||
# ---
|
||||
# name: test_entities[switch.mock_title_siren_alarm-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
||||
Reference in New Issue
Block a user