From 7f480849e23a49666fe6c8d84e43726948b26314 Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sun, 4 Jun 2023 20:28:28 -0400 Subject: [PATCH] Add camera platform to Dremel (#93882) * Add camera platform to Dremel * unload and tests --- .../components/dremel_3d_printer/__init__.py | 17 ++++--- .../components/dremel_3d_printer/camera.py | 44 +++++++++++++++++++ .../components/dremel_3d_printer/const.py | 2 + .../components/dremel_3d_printer/test_init.py | 18 +++++--- 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 homeassistant/components/dremel_3d_printer/camera.py diff --git a/homeassistant/components/dremel_3d_printer/__init__.py b/homeassistant/components/dremel_3d_printer/__init__.py index eaf22383839..db17e594cc4 100644 --- a/homeassistant/components/dremel_3d_printer/__init__.py +++ b/homeassistant/components/dremel_3d_printer/__init__.py @@ -9,10 +9,10 @@ from homeassistant.const import CONF_HOST, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from .const import DOMAIN +from .const import CAMERA_MODEL, DOMAIN from .coordinator import Dremel3DPrinterDataUpdateCoordinator -PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.CAMERA, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: @@ -30,12 +30,19 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b coordinator = Dremel3DPrinterDataUpdateCoordinator(hass, api) await coordinator.async_config_entry_first_refresh() hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = coordinator - await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS) + platforms = list(PLATFORMS) + if api.get_model() != CAMERA_MODEL: + platforms.remove(Platform.CAMERA) + await hass.config_entries.async_forward_entry_setups(config_entry, platforms) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload Dremel config entry.""" - if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): - hass.data.pop(DOMAIN) + platforms = list(PLATFORMS) + api: Dremel3DPrinter = hass.data[DOMAIN][entry.entry_id].api + if api.get_model() != CAMERA_MODEL: + platforms.remove(Platform.CAMERA) + if unload_ok := await hass.config_entries.async_unload_platforms(entry, platforms): + hass.data[DOMAIN].pop(entry.entry_id) return unload_ok diff --git a/homeassistant/components/dremel_3d_printer/camera.py b/homeassistant/components/dremel_3d_printer/camera.py new file mode 100644 index 00000000000..7468400ec35 --- /dev/null +++ b/homeassistant/components/dremel_3d_printer/camera.py @@ -0,0 +1,44 @@ +"""Support for Dremel 3D45 Camera.""" +from __future__ import annotations + +from homeassistant.components.camera import CameraEntityDescription +from homeassistant.components.mjpeg import MjpegCamera +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import Dremel3DPrinterDataUpdateCoordinator +from .const import DOMAIN +from .entity import Dremel3DPrinterEntity + +CAMERA_TYPE = CameraEntityDescription( + key="camera", + name="Camera", +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up a MJPEG IP Camera for the 3D45 Model. The 3D20 and 3D40 models don't have built in cameras.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id] + async_add_entities([Dremel3D45Camera(coordinator, CAMERA_TYPE)]) + + +class Dremel3D45Camera(Dremel3DPrinterEntity, MjpegCamera): + """Dremel 3D45 Camera.""" + + def __init__( + self, + coordinator: Dremel3DPrinterDataUpdateCoordinator, + description: CameraEntityDescription, + ) -> None: + """Initialize a new Dremel 3D Printer integration camera for the 3D45 model.""" + super().__init__(coordinator, description) + MjpegCamera.__init__( + self, + mjpeg_url=coordinator.api.get_stream_url(), + still_image_url=coordinator.api.get_snapshot_url(), + ) diff --git a/homeassistant/components/dremel_3d_printer/const.py b/homeassistant/components/dremel_3d_printer/const.py index 611b3b86306..cccdeb937cb 100644 --- a/homeassistant/components/dremel_3d_printer/const.py +++ b/homeassistant/components/dremel_3d_printer/const.py @@ -5,6 +5,8 @@ import logging LOGGER = logging.getLogger(__package__) +CAMERA_MODEL = "3D45" + DOMAIN = "dremel_3d_printer" ATTR_EXTRUDER = "extruder" diff --git a/tests/components/dremel_3d_printer/test_init.py b/tests/components/dremel_3d_printer/test_init.py index 5d97c89b9cd..a77c6159927 100644 --- a/tests/components/dremel_3d_printer/test_init.py +++ b/tests/components/dremel_3d_printer/test_init.py @@ -2,6 +2,7 @@ from datetime import timedelta from unittest.mock import patch +import pytest from requests.exceptions import ConnectTimeout from homeassistant.components.dremel_3d_printer.const import DOMAIN @@ -14,20 +15,27 @@ import homeassistant.util.dt as dt_util from tests.common import MockConfigEntry, async_fire_time_changed +MOCKED_MODEL = "homeassistant.components.dremel_3d_printer.Dremel3DPrinter.get_model" + +@pytest.mark.parametrize("model", ["3D45", "3D20"]) async def test_setup( - hass: HomeAssistant, connection, config_entry: MockConfigEntry + hass: HomeAssistant, connection, config_entry: MockConfigEntry, model: str ) -> None: """Test load and unload.""" - await hass.config_entries.async_setup(config_entry.entry_id) - assert await async_setup_component(hass, DOMAIN, {}) + with patch(MOCKED_MODEL, return_value=model) as mock: + await hass.config_entries.async_setup(config_entry.entry_id) + assert await async_setup_component(hass, DOMAIN, {}) assert config_entry.state == ConfigEntryState.LOADED + assert mock.called - assert await hass.config_entries.async_unload(config_entry.entry_id) - await hass.async_block_till_done() + with patch(MOCKED_MODEL, return_value=model) as mock: + assert await hass.config_entries.async_unload(config_entry.entry_id) + await hass.async_block_till_done() assert config_entry.state is ConfigEntryState.NOT_LOADED assert not hass.data.get(DOMAIN) + assert mock.called async def test_async_setup_entry_not_ready(