diff --git a/homeassistant/components/apple_tv/__init__.py b/homeassistant/components/apple_tv/__init__.py index 2448ddd06aa..09b11f555cf 100644 --- a/homeassistant/components/apple_tv/__init__.py +++ b/homeassistant/components/apple_tv/__init__.py @@ -51,7 +51,7 @@ DEFAULT_NAME_HP = "HomePod" BACKOFF_TIME_LOWER_LIMIT = 15 # seconds BACKOFF_TIME_UPPER_LIMIT = 300 # Five minutes -PLATFORMS = [Platform.MEDIA_PLAYER, Platform.REMOTE] +PLATFORMS = [Platform.BINARY_SENSOR, Platform.MEDIA_PLAYER, Platform.REMOTE] AUTH_EXCEPTIONS = ( exceptions.AuthenticationError, diff --git a/homeassistant/components/apple_tv/binary_sensor.py b/homeassistant/components/apple_tv/binary_sensor.py new file mode 100644 index 00000000000..0c4127ae6c1 --- /dev/null +++ b/homeassistant/components/apple_tv/binary_sensor.py @@ -0,0 +1,63 @@ +"""Binary sensor support for Apple TV.""" + +from __future__ import annotations + +from pyatv.const import KeyboardFocusState +from pyatv.interface import AppleTV, KeyboardListener + +from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from . import AppleTvConfigEntry +from .entity import AppleTVEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: AppleTvConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Load Apple TV binary sensor based on a config entry.""" + # apple_tv config entries always have a unique id + assert config_entry.unique_id is not None + name: str = config_entry.data[CONF_NAME] + manager = config_entry.runtime_data + async_add_entities([AppleTVKeyboardFocused(name, config_entry.unique_id, manager)]) + + +class AppleTVKeyboardFocused(AppleTVEntity, BinarySensorEntity, KeyboardListener): + """Binary sensor for Text input focused.""" + + _attr_translation_key = "keyboard_focused" + _attr_available = True + + @callback + def async_device_connected(self, atv: AppleTV) -> None: + """Handle when connection is made to device.""" + self._attr_available = True + # Listen to keyboard updates + atv.keyboard.listener = self + # Set initial state based on current focus state + self._update_state(atv.keyboard.text_focus_state == KeyboardFocusState.Focused) + + @callback + def async_device_disconnected(self) -> None: + """Handle when connection was lost to device.""" + self._attr_available = False + self._update_state(False) + + def focusstate_update( + self, old_state: KeyboardFocusState, new_state: KeyboardFocusState + ) -> None: + """Update keyboard state when it changes. + + This is a callback function from pyatv.interface.KeyboardListener. + """ + self._update_state(new_state == KeyboardFocusState.Focused) + + def _update_state(self, new_state: bool) -> None: + """Update and report.""" + self._attr_is_on = new_state + self.async_write_ha_state() diff --git a/homeassistant/components/apple_tv/entity.py b/homeassistant/components/apple_tv/entity.py index ad8364e2927..30f8a1ed939 100644 --- a/homeassistant/components/apple_tv/entity.py +++ b/homeassistant/components/apple_tv/entity.py @@ -18,7 +18,6 @@ class AppleTVEntity(Entity): _attr_should_poll = False _attr_has_entity_name = True - _attr_name = None atv: AppleTVInterface | None = None def __init__(self, name: str, identifier: str, manager: AppleTVManager) -> None: diff --git a/homeassistant/components/apple_tv/icons.json b/homeassistant/components/apple_tv/icons.json new file mode 100644 index 00000000000..8acb855e3c7 --- /dev/null +++ b/homeassistant/components/apple_tv/icons.json @@ -0,0 +1,12 @@ +{ + "entity": { + "binary_sensor": { + "keyboard_focused": { + "default": "mdi:keyboard", + "state": { + "off": "mdi:keyboard-off" + } + } + } + } +} diff --git a/homeassistant/components/apple_tv/media_player.py b/homeassistant/components/apple_tv/media_player.py index 1fedf1f524c..aa3be980625 100644 --- a/homeassistant/components/apple_tv/media_player.py +++ b/homeassistant/components/apple_tv/media_player.py @@ -115,6 +115,7 @@ class AppleTvMediaPlayer( """Representation of an Apple TV media player.""" _attr_supported_features = SUPPORT_APPLE_TV + _attr_name = None def __init__(self, name: str, identifier: str, manager: AppleTVManager) -> None: """Initialize the Apple TV media player.""" diff --git a/homeassistant/components/apple_tv/remote.py b/homeassistant/components/apple_tv/remote.py index 97e31bd4bb0..8b8910797bf 100644 --- a/homeassistant/components/apple_tv/remote.py +++ b/homeassistant/components/apple_tv/remote.py @@ -51,6 +51,8 @@ async def async_setup_entry( class AppleTVRemote(AppleTVEntity, RemoteEntity): """Device that sends commands to an Apple TV.""" + _attr_name = None + @property def is_on(self) -> bool: """Return true if device is on.""" diff --git a/homeassistant/components/apple_tv/strings.json b/homeassistant/components/apple_tv/strings.json index f1efedde150..98ff4b9acb7 100644 --- a/homeassistant/components/apple_tv/strings.json +++ b/homeassistant/components/apple_tv/strings.json @@ -62,6 +62,13 @@ } } }, + "entity": { + "binary_sensor": { + "keyboard_focused": { + "name": "Keyboard focus" + } + } + }, "options": { "step": { "init": {