diff --git a/homeassistant/components/bosch_shc/strings.json b/homeassistant/components/bosch_shc/strings.json index 72abebca1673..d15ea4dec941 100644 --- a/homeassistant/components/bosch_shc/strings.json +++ b/homeassistant/components/bosch_shc/strings.json @@ -98,6 +98,9 @@ "bypass_infinite": { "name": "Break function never expires" }, + "camera_light": { + "name": "Camera light" + }, "child_lock": { "name": "Child lock" }, diff --git a/homeassistant/components/bosch_shc/switch.py b/homeassistant/components/bosch_shc/switch.py index f808709950ed..bb536330affa 100644 --- a/homeassistant/components/bosch_shc/switch.py +++ b/homeassistant/components/bosch_shc/switch.py @@ -70,6 +70,15 @@ SWITCH_TYPES: dict[str, SHCSwitchEntityDescription] = { on_value=CameraLightService.State.ON, should_poll=True, ), + "cameraeyes_cameralight": SHCSwitchEntityDescription( + key="cameraeyes_cameralight", + translation_key="camera_light", + device_class=SwitchDeviceClass.SWITCH, + entity_category=EntityCategory.CONFIG, + on_key="cameralight", + on_value=CameraLightService.State.ON, + should_poll=True, + ), "camera360": SHCSwitchEntityDescription( key="camera360", device_class=SwitchDeviceClass.SWITCH, @@ -277,6 +286,18 @@ async def async_setup_entry( for switch in session.device_helper.camera_eyes ) + entities.extend( + SHCSwitch( + hass=hass, + device=switch, + parent_id=shc_info.unique_id, + entry_id=config_entry.entry_id, + description=SWITCH_TYPES["cameraeyes_cameralight"], + unique_id_suffix="cameraeyes_cameralight", + ) + for switch in session.device_helper.camera_eyes + ) + entities.extend( SHCSwitch( hass=hass, diff --git a/tests/components/bosch_shc/conftest.py b/tests/components/bosch_shc/conftest.py index 1cc265c03c15..a52aa13b9cc2 100644 --- a/tests/components/bosch_shc/conftest.py +++ b/tests/components/bosch_shc/conftest.py @@ -8,9 +8,11 @@ from unittest.mock import MagicMock, create_autospec, patch from boschshcpy import ( BatteryLevelService, BypassService, + CameraLightService, PowerSwitchService, RoutingService, SHCBatteryDevice, + SHCCameraEyes, SHCLightSwitchBSM, SHCMicromoduleBlinds, SHCMicromoduleRelay, @@ -162,6 +164,26 @@ def battery_only_device( return device +def camera_eyes_device( + device_id: str = "hdm:Cameras:eyes-1", + name: str = "Camera Eyes", + cameralight: CameraLightService.State = CameraLightService.State.OFF, +) -> SHCCameraEyes: + """Build a minimal device double for the camera_eyes bucket.""" + device = create_autospec(SHCCameraEyes, instance=True, spec_set=True) + device.name = name + device.id = device_id + device.root_device_id = "test-mac" + device.serial = f"serial-{device_id}" + device.manufacturer = "Bosch" + device.device_model = "CAMERA_EYES" + device.device_services = [] + device.deleted = False + device.status = "AVAILABLE" + device.cameralight = cameralight + return device + + def shutter_control_device( device_id: str = "hdm:ZigBee:shutter1", name: str = "Shutter", diff --git a/tests/components/bosch_shc/test_switch.py b/tests/components/bosch_shc/test_switch.py index c61d55cb2ee9..95b02b424054 100644 --- a/tests/components/bosch_shc/test_switch.py +++ b/tests/components/bosch_shc/test_switch.py @@ -3,7 +3,12 @@ from collections.abc import Generator from unittest.mock import MagicMock, patch -from boschshcpy import BypassService, SilentModeService, ThermostatService +from boschshcpy import ( + BypassService, + CameraLightService, + SilentModeService, + ThermostatService, +) import pytest from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN @@ -17,6 +22,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from .conftest import ( + camera_eyes_device, light_switch_bsm_device, micromodule_relay_device, motion_detector2_device, @@ -1001,3 +1007,39 @@ async def test_micromodule_relay_no_switch_configuration_support( assert hass.states.get("switch.relay_swap_inputs") is None assert hass.states.get("switch.relay_swap_outputs") is None + + +@pytest.mark.parametrize( + "device_buckets", + [{"camera_eyes": [camera_eyes_device(cameralight=CameraLightService.State.OFF)]}], + indirect=True, +) +@pytest.mark.usefixtures("mock_session") +async def test_camera_eyes_cameralight( + hass: HomeAssistant, + mock_session: MagicMock, + mock_config_entry: MockConfigEntry, +) -> None: + """A Camera Eyes' camera light is exposed and controllable as a switch.""" + await setup_integration(hass, mock_config_entry) + device = mock_session.device_helper.camera_eyes[0] + + state = hass.states.get("switch.camera_eyes_camera_light") + assert state is not None + assert state.state == "off" + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: "switch.camera_eyes_camera_light"}, + blocking=True, + ) + assert device.cameralight is True + + await hass.services.async_call( + SWITCH_DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: "switch.camera_eyes_camera_light"}, + blocking=True, + ) + assert device.cameralight is False