Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions homeassistant/components/homekit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
STATUS_STOPPED = 2
STATUS_WAIT = 3

PLATFORMS = ["sensor"]


def _has_all_unique_names_and_ports(bridges):
"""Validate that each homekit bridge configured has a unique name."""
Expand Down Expand Up @@ -234,7 +236,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
safe_mode,
advertise_ip,
interface_choice,
entry.entry_id,
entry,
)
await hass.async_add_executor_job(homekit.setup)

Expand Down Expand Up @@ -271,7 +273,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
homekit = hass.data[DOMAIN][entry.entry_id][HOMEKIT]

if homekit.status == STATUS_RUNNING:
await homekit.async_stop()
unload_ok = await homekit.async_stop()

for _ in range(0, SHUTDOWN_TIMEOUT):
if not await hass.async_add_executor_job(
Expand All @@ -280,9 +282,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
_LOGGER.info("Waiting for the HomeKit server to shutdown.")
await asyncio.sleep(1)

hass.data[DOMAIN].pop(entry.entry_id)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)

return True
return unload_ok


async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry):
Expand Down Expand Up @@ -389,7 +392,7 @@ def __init__(
safe_mode,
advertise_ip=None,
interface_choice=None,
entry_id=None,
config_entry=None,
):
"""Initialize a HomeKit object."""
self.hass = hass
Expand All @@ -401,7 +404,8 @@ def __init__(
self._safe_mode = safe_mode
self._advertise_ip = advertise_ip
self._interface_choice = interface_choice
self._entry_id = entry_id
self._config_entry = config_entry
self._entry_id = config_entry.entry_id
self.status = STATUS_READY

self.bridge = None
Expand Down Expand Up @@ -513,11 +517,19 @@ async def async_start(self, *args):
await self.hass.async_add_executor_job(self._start, bridged_states)
await self._async_register_bridge()

for component in PLATFORMS:
self.hass.async_create_task(
self.hass.config_entries.async_forward_entry_setup(
self._config_entry, component
)
)

async def _async_register_bridge(self):
"""Register the bridge as a device so homekit_controller and exclude it from discovery."""
registry = await device_registry.async_get_registry(self.hass)
registry.async_get_or_create(
config_entry_id=self._entry_id,
identifiers={(DOMAIN, self.driver.state.mac)},
connections={
(device_registry.CONNECTION_NETWORK_MAC, self.driver.state.mac)
},
Expand Down Expand Up @@ -565,6 +577,16 @@ async def async_stop(self, *args):
self.status = STATUS_STOPPED
_LOGGER.debug("Driver stop for %s", self._name)
self.hass.add_job(self.driver.stop)
return all(
await asyncio.gather(
*[
self.hass.config_entries.async_forward_entry_unload(
self._config_entry, component
)
for component in PLATFORMS
]
)
)

@callback
def _async_configure_linked_battery_sensors(self, ent_reg, device_lookup, state):
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/homekit/accessories.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ def __init__(
self.config = config or {}
self.entity_id = entity_id
self.hass = hass
self.name = name
self.debounce = {}
self._char_battery = None
self._char_charging = None
Expand Down
41 changes: 41 additions & 0 deletions homeassistant/components/homekit/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""The homekitintegration base entity."""

from homeassistant.helpers.entity import Entity

from .const import DOMAIN, MANUFACTURER


class HomeKitEntity(Entity):
"""Base class for homekit entities."""

def __init__(self, bridge_mac, acc, model):
"""Initialize the sensor."""
super().__init__()
self.model = model
self.acc = acc
self.bridge_mac = bridge_mac
self.base_unique_id = f"{bridge_mac}_{acc.aid}"

@property
def device_info(self):
"""Entity device info."""
device_info = {
"identifiers": {(DOMAIN, self.base_unique_id)},
"name": self.name,
"manufacturer": MANUFACTURER,
"model": self.model,
"via_device": (DOMAIN, self.bridge_mac),
}
return device_info

@property
def should_poll(self):
"""Update from callback."""
return False

async def async_remove(self):
"""Remove from entity registry on remove."""
entity_registry = await self.hass.helpers.entity_registry.async_get_registry()
if entity_registry.async_get(self.entity_id):
entity_registry.async_remove(self.entity_id)
return await super().async_remove()
66 changes: 66 additions & 0 deletions homeassistant/components/homekit/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Support for homekit sensors."""
import logging

from homeassistant.core import callback

from .const import DOMAIN, HOMEKIT
from .entity import HomeKitEntity
from .type_media_players import TelevisionMediaPlayer

_LOGGER = logging.getLogger(__name__)

HOMEKIT_REMOTE_MODEL = "HomeKit Remote"


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the homekit sensors."""

homekit = hass.data[DOMAIN][config_entry.entry_id][HOMEKIT]
mac = homekit.driver.state.mac
entities = []

for acc in homekit.bridge.accessories.values():
if isinstance(acc, TelevisionMediaPlayer):
entities.append(RemoteKeySensor(mac, acc))

async_add_entities(entities, False)


class RemoteKeySensor(HomeKitEntity):
Comment thread
bdraco marked this conversation as resolved.
"""Representation of an homekit remote control sensor."""

def __init__(self, mac, acc):
"""Init the remote control sensor."""
super().__init__(mac, acc, HOMEKIT_REMOTE_MODEL)
self._state = None

@property
def name(self):
"""Sensor Name."""
return f"{self.acc.name} {self.model}"

@property
def unique_id(self):
"""Sensor Uniqueid."""
return f"{self.base_unique_id}_remote"

@property
def state(self):
"""Key pressed."""
return self._state

@callback
def _async_key_pressed(self, value):
"""Update the sensor with the keypress."""
# Press
self._state = value
self.async_write_ha_state()
# .. and release
self._state = None
self.async_write_ha_state()

async def async_added_to_hass(self):
"""Subscribe to updates."""
self.async_on_remove(
self.acc.async_add_remote_key_listener(self._async_key_pressed)
)
26 changes: 25 additions & 1 deletion homeassistant/components/homekit/type_media_players.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Class to hold all media player accessories."""
import logging
from typing import Callable

from pyhap.const import CATEGORY_SWITCH, CATEGORY_TELEVISION

Expand Down Expand Up @@ -36,6 +37,7 @@
STATE_STANDBY,
STATE_UNKNOWN,
)
from homeassistant.core import CALLBACK_TYPE, callback

from .accessories import TYPES, HomeAccessory
from .const import (
Expand Down Expand Up @@ -238,7 +240,7 @@ def __init__(self, *args):
state = self.hass.states.get(self.entity_id)

self.support_select_source = False

self._remote_key_listeners = []
self.sources = []

# Add additional characteristics if volume or input selection supported
Expand Down Expand Up @@ -356,9 +358,31 @@ def set_input_source(self, value):
params = {ATTR_ENTITY_ID: self.entity_id, ATTR_INPUT_SOURCE: source}
self.call_service(DOMAIN, SERVICE_SELECT_SOURCE, params)

@callback
def async_add_remote_key_listener(
self, update_callback: CALLBACK_TYPE
) -> Callable[[], None]:
"""Listen for remote key updates."""
self._remote_key_listeners.append(update_callback)

@callback
def remove_listener() -> None:
"""Remove remote key listener."""
self.async_remove_remote_key_listener(update_callback)

return remove_listener

@callback
def async_remove_remote_key_listener(self, update_callback: CALLBACK_TYPE) -> None:
"""Remove remote key listener."""
self._remote_key_listeners.remove(update_callback)

def set_remote_key(self, value):
"""Send remote key value if call came from HomeKit."""
_LOGGER.debug("%s: Set remote key to %s", self.entity_id, value)
for update_callback in self._remote_key_listeners:
update_callback(value)

service = MEDIA_PLAYER_KEYS.get(value)
if service:
# Handle Play Pause
Expand Down