Add support for philips js screen state (#62775)
parent
06f05d2302
commit
7c6297db86
|
@ -827,6 +827,7 @@ omit =
|
|||
homeassistant/components/philips_js/light.py
|
||||
homeassistant/components/philips_js/media_player.py
|
||||
homeassistant/components/philips_js/remote.py
|
||||
homeassistant/components/philips_js/switch.py
|
||||
homeassistant/components/pi_hole/sensor.py
|
||||
homeassistant/components/pi4ioe5v9xxxx/binary_sensor.py
|
||||
homeassistant/components/pi4ioe5v9xxxx/switch.py
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Mapping
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from haphilipsjs import ConnectionFailure, PhilipsTV
|
||||
from haphilipsjs.typing import SystemType
|
||||
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
|
@ -18,13 +19,25 @@ from homeassistant.const import (
|
|||
CONF_USERNAME,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import CALLBACK_TYPE, Context, HassJob, HomeAssistant, callback
|
||||
from homeassistant.core import (
|
||||
CALLBACK_TYPE,
|
||||
Context,
|
||||
Event,
|
||||
HassJob,
|
||||
HomeAssistant,
|
||||
callback,
|
||||
)
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import CONF_ALLOW_NOTIFY, DOMAIN
|
||||
from .const import CONF_ALLOW_NOTIFY, CONF_SYSTEM, DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.MEDIA_PLAYER, Platform.LIGHT, Platform.REMOTE]
|
||||
PLATFORMS = [
|
||||
Platform.MEDIA_PLAYER,
|
||||
Platform.LIGHT,
|
||||
Platform.REMOTE,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -71,7 +84,7 @@ class PluggableAction:
|
|||
def __init__(self, update: Callable[[], None]) -> None:
|
||||
"""Initialize."""
|
||||
self._update = update
|
||||
self._actions: dict[Any, AutomationActionType] = {}
|
||||
self._actions: dict[Any, tuple[HassJob, dict[str, Any]]] = {}
|
||||
|
||||
def __bool__(self):
|
||||
"""Return if we have something attached."""
|
||||
|
@ -102,7 +115,7 @@ class PluggableAction:
|
|||
class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
||||
"""Coordinator to update data."""
|
||||
|
||||
def __init__(self, hass, api: PhilipsTV, options: dict) -> None:
|
||||
def __init__(self, hass, api: PhilipsTV, options: Mapping) -> None:
|
||||
"""Set up the coordinator."""
|
||||
self.api = api
|
||||
self.options = options
|
||||
|
@ -125,6 +138,20 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def system(self) -> SystemType:
|
||||
"""Return the system descriptor."""
|
||||
if self.api.system:
|
||||
return self.api.system
|
||||
return self.config_entry.data[CONF_SYSTEM]
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return the system descriptor."""
|
||||
assert self.config_entry
|
||||
assert self.config_entry.unique_id
|
||||
return self.config_entry.unique_id
|
||||
|
||||
@property
|
||||
def _notify_wanted(self):
|
||||
"""Return if the notify feature should be active.
|
||||
|
@ -170,7 +197,7 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||
self._async_notify_stop()
|
||||
|
||||
@callback
|
||||
def _async_stop_refresh(self, event: asyncio.Event) -> None:
|
||||
def _async_stop_refresh(self, event: Event) -> None:
|
||||
super()._async_stop_refresh(event)
|
||||
self._async_notify_stop()
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
"""Component to integrate ambilight for TVs exposing the Joint Space API."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from haphilipsjs import PhilipsTV
|
||||
from haphilipsjs.typing import AmbilightCurrentConfiguration
|
||||
|
||||
|
@ -24,7 +22,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
|||
from homeassistant.util.color import color_hsv_to_RGB, color_RGB_to_hsv
|
||||
|
||||
from . import PhilipsTVDataUpdateCoordinator
|
||||
from .const import CONF_SYSTEM, DOMAIN
|
||||
from .const import DOMAIN
|
||||
|
||||
EFFECT_PARTITION = ": "
|
||||
EFFECT_MODE = "Mode"
|
||||
|
@ -40,13 +38,7 @@ async def async_setup_entry(
|
|||
):
|
||||
"""Set up the configuration entry."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
async_add_entities(
|
||||
[
|
||||
PhilipsTVLightEntity(
|
||||
coordinator, config_entry.data[CONF_SYSTEM], config_entry.unique_id
|
||||
)
|
||||
]
|
||||
)
|
||||
async_add_entities([PhilipsTVLightEntity(coordinator)])
|
||||
|
||||
|
||||
def _get_settings(style: AmbilightCurrentConfiguration):
|
||||
|
@ -136,15 +128,11 @@ class PhilipsTVLightEntity(CoordinatorEntity, LightEntity):
|
|||
def __init__(
|
||||
self,
|
||||
coordinator: PhilipsTVDataUpdateCoordinator,
|
||||
system: dict[str, Any],
|
||||
unique_id: str,
|
||||
) -> None:
|
||||
"""Initialize light."""
|
||||
self._tv = coordinator.api
|
||||
self._hs = None
|
||||
self._brightness = None
|
||||
self._system = system
|
||||
self._coordinator = coordinator
|
||||
self._cache_keys = None
|
||||
super().__init__(coordinator)
|
||||
|
||||
|
@ -152,17 +140,17 @@ class PhilipsTVLightEntity(CoordinatorEntity, LightEntity):
|
|||
self._attr_supported_features = (
|
||||
SUPPORT_EFFECT | SUPPORT_COLOR | SUPPORT_BRIGHTNESS
|
||||
)
|
||||
self._attr_name = self._system["name"]
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_name = f"{coordinator.system['name']} Ambilight"
|
||||
self._attr_unique_id = coordinator.unique_id
|
||||
self._attr_icon = "mdi:television-ambient-light"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, self._attr_unique_id),
|
||||
},
|
||||
manufacturer="Philips",
|
||||
model=self._system.get("model"),
|
||||
name=self._system["name"],
|
||||
sw_version=self._system.get("softwareversion"),
|
||||
model=coordinator.system.get("model"),
|
||||
name=coordinator.system["name"],
|
||||
sw_version=coordinator.system.get("softwareversion"),
|
||||
)
|
||||
|
||||
self._update_from_coordinator()
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
"""Media Player component to integrate TVs exposing the Joint Space API."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from haphilipsjs import ConnectionFailure
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
@ -40,7 +38,7 @@ from homeassistant.helpers.entity import DeviceInfo
|
|||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import LOGGER as _LOGGER, PhilipsTVDataUpdateCoordinator
|
||||
from .const import CONF_SYSTEM, DOMAIN
|
||||
from .const import DOMAIN
|
||||
|
||||
SUPPORT_PHILIPS_JS = (
|
||||
SUPPORT_TURN_OFF
|
||||
|
@ -75,8 +73,6 @@ async def async_setup_entry(
|
|||
[
|
||||
PhilipsTVMediaPlayer(
|
||||
coordinator,
|
||||
config_entry.data[CONF_SYSTEM],
|
||||
config_entry.unique_id,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
@ -85,13 +81,12 @@ async def async_setup_entry(
|
|||
class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity):
|
||||
"""Representation of a Philips TV exposing the JointSpace API."""
|
||||
|
||||
_coordinator: PhilipsTVDataUpdateCoordinator
|
||||
_attr_device_class = MediaPlayerDeviceClass.TV
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PhilipsTVDataUpdateCoordinator,
|
||||
system: dict[str, Any],
|
||||
unique_id: str,
|
||||
) -> None:
|
||||
"""Initialize the Philips TV."""
|
||||
self._tv = coordinator.api
|
||||
|
@ -99,8 +94,18 @@ class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity):
|
|||
self._sources = {}
|
||||
self._channels = {}
|
||||
self._supports = SUPPORT_PHILIPS_JS
|
||||
self._system = system
|
||||
self._unique_id = unique_id
|
||||
self._system = coordinator.system
|
||||
self._attr_name = coordinator.system["name"]
|
||||
self._attr_unique_id = coordinator.unique_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, coordinator.unique_id),
|
||||
},
|
||||
manufacturer="Philips",
|
||||
model=coordinator.system.get("model"),
|
||||
sw_version=coordinator.system.get("softwareversion"),
|
||||
name=coordinator.system["name"],
|
||||
)
|
||||
self._state = STATE_OFF
|
||||
self._media_content_type: str | None = None
|
||||
self._media_content_id: str | None = None
|
||||
|
@ -115,11 +120,6 @@ class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity):
|
|||
self.async_write_ha_state()
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the device name."""
|
||||
return self._system["name"]
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag media player features that are supported."""
|
||||
|
@ -280,24 +280,6 @@ class PhilipsTVMediaPlayer(CoordinatorEntity, MediaPlayerEntity):
|
|||
if app := self._tv.applications.get(self._tv.application_id):
|
||||
return app.get("label")
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique identifier if known."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a device description for device registry."""
|
||||
return DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, self._unique_id),
|
||||
},
|
||||
manufacturer="Philips",
|
||||
model=self._system.get("model"),
|
||||
sw_version=self._system.get("softwareversion"),
|
||||
name=self._system["name"],
|
||||
)
|
||||
|
||||
async def async_play_media(self, media_type, media_id, **kwargs):
|
||||
"""Play a piece of media."""
|
||||
_LOGGER.debug("Call play media type <%s>, Id <%s>", media_type, media_id)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
"""Remote control support for Apple TV."""
|
||||
import asyncio
|
||||
|
||||
from haphilipsjs.typing import SystemType
|
||||
|
||||
from homeassistant.components.remote import (
|
||||
ATTR_DELAY_SECS,
|
||||
ATTR_NUM_REPEATS,
|
||||
|
@ -13,9 +11,10 @@ from homeassistant.config_entries import ConfigEntry
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import LOGGER, PhilipsTVDataUpdateCoordinator
|
||||
from .const import CONF_SYSTEM, DOMAIN
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -25,36 +24,32 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up the configuration entry."""
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id]
|
||||
async_add_entities(
|
||||
[
|
||||
PhilipsTVRemote(
|
||||
coordinator,
|
||||
config_entry.data[CONF_SYSTEM],
|
||||
config_entry.unique_id,
|
||||
)
|
||||
]
|
||||
)
|
||||
async_add_entities([PhilipsTVRemote(coordinator)])
|
||||
|
||||
|
||||
class PhilipsTVRemote(RemoteEntity):
|
||||
class PhilipsTVRemote(CoordinatorEntity, RemoteEntity):
|
||||
"""Device that sends commands."""
|
||||
|
||||
_coordinator: PhilipsTVDataUpdateCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PhilipsTVDataUpdateCoordinator,
|
||||
system: SystemType,
|
||||
unique_id: str,
|
||||
) -> None:
|
||||
"""Initialize the Philips TV."""
|
||||
super().__init__(coordinator)
|
||||
self._tv = coordinator.api
|
||||
self._coordinator = coordinator
|
||||
self._system = system
|
||||
self._unique_id = unique_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the device name."""
|
||||
return self._system["name"]
|
||||
self._attr_name = f"{coordinator.system['name']} Remote"
|
||||
self._attr_unique_id = coordinator.unique_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, coordinator.unique_id),
|
||||
},
|
||||
manufacturer="Philips",
|
||||
model=coordinator.system.get("model"),
|
||||
name=coordinator.system["name"],
|
||||
sw_version=coordinator.system.get("softwareversion"),
|
||||
)
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
|
@ -63,29 +58,6 @@ class PhilipsTVRemote(RemoteEntity):
|
|||
self._tv.on and (self._tv.powerstate == "On" or self._tv.powerstate is None)
|
||||
)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed for Apple TV."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique identifier if known."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return a device description for device registry."""
|
||||
return DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, self._unique_id),
|
||||
},
|
||||
manufacturer="Philips",
|
||||
model=self._system.get("model"),
|
||||
name=self._system["name"],
|
||||
sw_version=self._system.get("softwareversion"),
|
||||
)
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Turn the device on."""
|
||||
if self._tv.on and self._tv.powerstate:
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
"""Philips TV menu switches."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import PhilipsTVDataUpdateCoordinator
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
):
|
||||
"""Set up the configuration entry."""
|
||||
coordinator: PhilipsTVDataUpdateCoordinator = hass.data[DOMAIN][
|
||||
config_entry.entry_id
|
||||
]
|
||||
|
||||
async_add_entities([PhilipsTVScreenSwitch(coordinator)])
|
||||
|
||||
|
||||
class PhilipsTVScreenSwitch(CoordinatorEntity, SwitchEntity):
|
||||
"""A Philips TV screen state switch."""
|
||||
|
||||
coordinator: PhilipsTVDataUpdateCoordinator
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PhilipsTVDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize entity."""
|
||||
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._attr_name = f"{coordinator.system['name']} Screen State"
|
||||
self._attr_icon = "mdi:television-shimmer"
|
||||
self._attr_unique_id = f"{coordinator.unique_id}_screenstate"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={
|
||||
(DOMAIN, coordinator.unique_id),
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return true if entity is available."""
|
||||
if not super().available:
|
||||
return False
|
||||
if not self.coordinator.api.on:
|
||||
return False
|
||||
return self.coordinator.api.powerstate == "On"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return True if entity is on."""
|
||||
return self.coordinator.api.screenstate == "On"
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity on."""
|
||||
await self.coordinator.api.setScreenState("On")
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn the entity off."""
|
||||
await self.coordinator.api.setScreenState("Off")
|
|
@ -41,7 +41,9 @@ def mock_tv():
|
|||
@fixture
|
||||
async def mock_config_entry(hass):
|
||||
"""Get standard player."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, title=MOCK_NAME)
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=MOCK_CONFIG, title=MOCK_NAME, unique_id="ABCDEFGHIJKLF"
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
return config_entry
|
||||
|
||||
|
|
Loading…
Reference in New Issue