diff --git a/homeassistant/components/fully_kiosk/__init__.py b/homeassistant/components/fully_kiosk/__init__.py index a0ed0cb4fa0..95d7d59ecbf 100644 --- a/homeassistant/components/fully_kiosk/__init__.py +++ b/homeassistant/components/fully_kiosk/__init__.py @@ -13,6 +13,7 @@ from .services import async_setup_services PLATFORMS = [ Platform.BINARY_SENSOR, Platform.BUTTON, + Platform.CAMERA, Platform.MEDIA_PLAYER, Platform.NUMBER, Platform.SENSOR, diff --git a/homeassistant/components/fully_kiosk/camera.py b/homeassistant/components/fully_kiosk/camera.py new file mode 100644 index 00000000000..99419271c26 --- /dev/null +++ b/homeassistant/components/fully_kiosk/camera.py @@ -0,0 +1,56 @@ +"""Support for Fully Kiosk Browser camera.""" + +from __future__ import annotations + +from homeassistant.components.camera import Camera, CameraEntityFeature +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import FullyKioskDataUpdateCoordinator +from .entity import FullyKioskEntity + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up the cameras.""" + coordinator: FullyKioskDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + async_add_entities([FullyCameraEntity(coordinator)]) + + +class FullyCameraEntity(FullyKioskEntity, Camera): + """Fully Kiosk Browser camera entity.""" + + _attr_name = None + _attr_supported_features = CameraEntityFeature.ON_OFF + + def __init__(self, coordinator: FullyKioskDataUpdateCoordinator) -> None: + """Initialize the camera.""" + FullyKioskEntity.__init__(self, coordinator) + Camera.__init__(self) + self._attr_unique_id = f"{coordinator.data['deviceID']}-camera" + + async def async_camera_image( + self, width: int | None = None, height: int | None = None + ) -> bytes | None: + """Return bytes of camera image.""" + image_bytes: bytes = await self.coordinator.fully.getCamshot() + return image_bytes + + async def async_turn_on(self) -> None: + """Turn on camera.""" + await self.coordinator.fully.enableMotionDetection() + await self.coordinator.async_refresh() + + async def async_turn_off(self) -> None: + """Turn off camera.""" + await self.coordinator.fully.disableMotionDetection() + await self.coordinator.async_refresh() + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._attr_is_on = self.coordinator.data["settings"].get("motionDetection") + self.async_write_ha_state() diff --git a/tests/components/fully_kiosk/test_camera.py b/tests/components/fully_kiosk/test_camera.py new file mode 100644 index 00000000000..4e48749eebb --- /dev/null +++ b/tests/components/fully_kiosk/test_camera.py @@ -0,0 +1,55 @@ +"""Test the Fully Kiosk Browser camera platform.""" + +from unittest.mock import MagicMock + +import pytest + +from homeassistant.components.camera import async_get_image +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er + +from tests.common import MockConfigEntry + + +async def test_camera( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_fully_kiosk: MagicMock, + init_integration: MockConfigEntry, +) -> None: + """Test the camera entity.""" + entity_camera = "camera.amazon_fire" + entity = hass.states.get(entity_camera) + assert entity + assert entity.state == "idle" + entry = entity_registry.async_get(entity_camera) + assert entry + assert entry.unique_id == "abcdef-123456-camera" + + mock_fully_kiosk.getSettings.return_value = {"motionDetection": True} + await hass.services.async_call( + "camera", + "turn_on", + {"entity_id": entity_camera}, + blocking=True, + ) + assert len(mock_fully_kiosk.enableMotionDetection.mock_calls) == 1 + + mock_fully_kiosk.getCamshot.return_value = b"image_bytes" + image = await async_get_image(hass, entity_camera) + assert mock_fully_kiosk.getCamshot.call_count == 1 + assert image.content == b"image_bytes" + + mock_fully_kiosk.getSettings.return_value = {"motionDetection": False} + await hass.services.async_call( + "camera", + "turn_off", + {"entity_id": entity_camera}, + blocking=True, + ) + assert len(mock_fully_kiosk.disableMotionDetection.mock_calls) == 1 + + with pytest.raises(HomeAssistantError) as error: + await async_get_image(hass, entity_camera) + assert error.value.args[0] == "Camera is off" diff --git a/tests/components/fully_kiosk/test_media_player.py b/tests/components/fully_kiosk/test_media_player.py index b6eff4cfa2c..4ee9b595a82 100644 --- a/tests/components/fully_kiosk/test_media_player.py +++ b/tests/components/fully_kiosk/test_media_player.py @@ -113,6 +113,8 @@ async def test_browse_media( { "id": 1, "type": "media_player/browse_media", + "media_content_id": "media-source://media_source", + "media_content_type": "library", "entity_id": "media_player.amazon_fire", } )