1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Add JVC Projector switch platform (#161899)

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Steve Easley
2026-02-12 08:21:34 -05:00
committed by GitHub
parent 0388e5dd7f
commit 48893d4daa
7 changed files with 280 additions and 1 deletions

View File

@@ -17,7 +17,13 @@ from homeassistant.helpers.entity_registry import RegistryEntry, async_migrate_e
from .coordinator import JVCConfigEntry, JvcProjectorDataUpdateCoordinator
PLATFORMS = [Platform.BINARY_SENSOR, Platform.REMOTE, Platform.SELECT, Platform.SENSOR]
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.REMOTE,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
]
async def async_setup_entry(hass: HomeAssistant, entry: JVCConfigEntry) -> bool:

View File

@@ -53,6 +53,14 @@
"warming": "mdi:heat-wave"
}
}
},
"switch": {
"eshift": {
"default": "mdi:blur-linear"
},
"low_latency_mode": {
"default": "mdi:gamepad-round-outline"
}
}
}
}

View File

@@ -173,6 +173,14 @@
"warming": "Warming"
}
}
},
"switch": {
"eshift": {
"name": "E-Shift"
},
"low_latency_mode": {
"name": "Low latency mode"
}
}
}
}

View File

@@ -0,0 +1,82 @@
"""Switch platform for the jvc_projector integration."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Final
from jvcprojector import Command, command as cmd
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import JVCConfigEntry, JvcProjectorDataUpdateCoordinator
from .entity import JvcProjectorEntity
@dataclass(frozen=True, kw_only=True)
class JvcProjectorSwitchDescription(SwitchEntityDescription):
"""Describes JVC Projector switch entities."""
command: type[Command]
SWITCHES: Final[tuple[JvcProjectorSwitchDescription, ...]] = (
JvcProjectorSwitchDescription(
key="low_latency_mode",
command=cmd.LowLatencyMode,
entity_registry_enabled_default=False,
),
JvcProjectorSwitchDescription(
key="eshift",
command=cmd.EShift,
entity_registry_enabled_default=False,
),
)
async def async_setup_entry(
hass: HomeAssistant,
entry: JVCConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the JVC Projector switch platform from a config entry."""
coordinator = entry.runtime_data
async_add_entities(
JvcProjectorSwitchEntity(coordinator, description)
for description in SWITCHES
if coordinator.supports(description.command)
)
class JvcProjectorSwitchEntity(JvcProjectorEntity, SwitchEntity):
"""JVC Projector class for switch entities."""
def __init__(
self,
coordinator: JvcProjectorDataUpdateCoordinator,
description: JvcProjectorSwitchDescription,
) -> None:
"""Initialize the entity."""
super().__init__(coordinator, description.command)
self.command: type[Command] = description.command
self.entity_description = description
self._attr_translation_key = description.key
self._attr_unique_id = f"{self._attr_unique_id}_{description.key}"
@property
def is_on(self) -> bool:
"""Return True if the entity is on."""
return self.coordinator.data.get(self.command.name) == STATE_ON
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
await self.coordinator.device.set(self.command, STATE_ON)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
await self.coordinator.device.set(self.command, STATE_OFF)

View File

@@ -26,6 +26,7 @@ FIXTURES: dict[str, dict[type[Command], str | type[Exception]]] = {
cmd.Source: JvcProjectorTimeoutError,
cmd.Hdr: JvcProjectorTimeoutError,
cmd.HdrProcessing: JvcProjectorTimeoutError,
cmd.EShift: JvcProjectorTimeoutError,
},
"on": {
cmd.MacAddress: MOCK_MAC,
@@ -37,6 +38,7 @@ FIXTURES: dict[str, dict[type[Command], str | type[Exception]]] = {
cmd.Source: "4k",
cmd.Hdr: "hdr",
cmd.HdrProcessing: "static",
cmd.EShift: "on",
},
}
@@ -69,6 +71,10 @@ CAPABILITIES = {
"name": cmd.LightTime.name,
"parameter": "empty",
},
cmd.EShift.name: {
"name": cmd.EShift.name,
"parameter": {"read": {"0": "off", "1": "on"}},
},
}

View File

@@ -0,0 +1,99 @@
# serializer version: 1
# name: test_entities[switch.jvc_projector_e_shift-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.jvc_projector_e_shift',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'E-Shift',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'E-Shift',
'platform': 'jvc_projector',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'eshift',
'unique_id': 'e0:da:dc:0a:12:34_eshift',
'unit_of_measurement': None,
})
# ---
# name: test_entities[switch.jvc_projector_e_shift-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'JVC Projector E-Shift',
}),
'context': <ANY>,
'entity_id': 'switch.jvc_projector_e_shift',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_entities[switch.jvc_projector_low_latency_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.jvc_projector_low_latency_mode',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Low latency mode',
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Low latency mode',
'platform': 'jvc_projector',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'low_latency_mode',
'unique_id': 'e0:da:dc:0a:12:34_low_latency_mode',
'unit_of_measurement': None,
})
# ---
# name: test_entities[switch.jvc_projector_low_latency_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'JVC Projector Low latency mode',
}),
'context': <ANY>,
'entity_id': 'switch.jvc_projector_low_latency_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@@ -0,0 +1,70 @@
"""Tests for JVC Projector switch platform."""
from collections.abc import Generator
from unittest.mock import AsyncMock, MagicMock, patch
from jvcprojector import command as cmd
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.switch import (
DOMAIN as SWITCH_DOMAIN,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
ESHIFT_ENTITY_ID = "switch.jvc_projector_e_shift"
LOW_LATENCY_ENTITY_ID = "switch.jvc_projector_low_latency_mode"
@pytest.fixture(autouse=True)
def platform() -> Generator[AsyncMock]:
"""Fixture for platform."""
with patch("homeassistant.components.jvc_projector.PLATFORMS", [Platform.SWITCH]):
yield
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_entities(
hass: HomeAssistant,
mock_device: MagicMock,
mock_integration: MockConfigEntry,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test entities."""
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_switch_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
mock_device: MagicMock,
mock_integration: MockConfigEntry,
) -> None:
"""Test switch entities."""
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: ESHIFT_ENTITY_ID},
blocking=True,
)
mock_device.set.assert_any_call(cmd.EShift, "off")
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ESHIFT_ENTITY_ID},
blocking=True,
)
mock_device.set.assert_any_call(cmd.EShift, "on")