361 files
This commit is contained in:
@@ -7,10 +7,13 @@ from typing import TYPE_CHECKING
|
||||
from homeassistant.const import CONF_ENTITY_ID
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.helper_integration import async_remove_helper_devices
|
||||
from homeassistant.helpers.helper_integration import (
|
||||
async_handle_source_entity_changes,
|
||||
async_remove_helper_devices,
|
||||
)
|
||||
|
||||
from .config_flow import SpookInverseConfigFlowHandler
|
||||
from .const import CONF_HIDE_SOURCE
|
||||
from .const import CONF_HIDE_SOURCE, DOMAIN
|
||||
|
||||
MIGRATION_MINOR_VERSION = SpookInverseConfigFlowHandler.MINOR_VERSION
|
||||
|
||||
@@ -36,6 +39,40 @@ def async_get_source_entity_device_id(
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up from a config entry."""
|
||||
source = entry.options[CONF_ENTITY_ID]
|
||||
|
||||
# Remembered for when the options change, which is the only moment the
|
||||
# previous source, and whether this inverse hid it, is still known.
|
||||
# Somebody pointing the inverse elsewhere would otherwise leave the old
|
||||
# one hidden for good.
|
||||
entry.runtime_data = (source, entry.options[CONF_HIDE_SOURCE])
|
||||
|
||||
@callback
|
||||
def _follow_the_source(source_entity_id: str) -> None:
|
||||
"""Keep up with the source being renamed, the way core helpers do.
|
||||
|
||||
The inverse listens to its source by entity ID, and a rename gives it
|
||||
a new one: without this it keeps listening to a name nobody uses and
|
||||
switching something that is not there.
|
||||
"""
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, options={**entry.options, CONF_ENTITY_ID: source_entity_id}
|
||||
)
|
||||
|
||||
# A source stored as a registry ID that is gone resolves to nothing, and
|
||||
# Home Assistant raises on following that. The inverse still sets up, and
|
||||
# is unavailable, which is the truth about it.
|
||||
if er.async_resolve_entity_id(er.async_get(hass), source):
|
||||
entry.async_on_unload(
|
||||
async_handle_source_entity_changes(
|
||||
hass,
|
||||
helper_config_entry_id=entry.entry_id,
|
||||
set_source_entity_id_or_uuid=_follow_the_source,
|
||||
source_device_id=async_get_source_entity_device_id(hass, source),
|
||||
source_entity_id_or_uuid=source,
|
||||
)
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
entry,
|
||||
(entry.options["inverse_type"],),
|
||||
@@ -45,10 +82,52 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
|
||||
async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener, called when the config entry options are changed."""
|
||||
"""Update listener, called when the config entry options are changed.
|
||||
|
||||
Hiding happens in the options flow. Showing the source again happens
|
||||
here, because only here are both the old options and the new ones known,
|
||||
and showing it again is only this inverse's to do if it was the one that
|
||||
hid it.
|
||||
"""
|
||||
previous_source, previously_hidden = entry.runtime_data
|
||||
if previously_hidden and (
|
||||
previous_source != entry.options[CONF_ENTITY_ID]
|
||||
or not entry.options[CONF_HIDE_SOURCE]
|
||||
):
|
||||
async_release_source(hass, previous_source)
|
||||
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
@callback
|
||||
def async_release_source(hass: HomeAssistant, source: str) -> None:
|
||||
"""Show a source again that this inverse hid and no longer hides.
|
||||
|
||||
Only if Spook hid it, and only if no other inverse still wants it hidden:
|
||||
two inverses of one source share the hiding, and one of them letting go
|
||||
is not the other one letting go. After a rename the old name is in the
|
||||
registry no more, and the entry that is, with its hiding, is left alone.
|
||||
"""
|
||||
registry = er.async_get(hass)
|
||||
if not (entity_id := er.async_resolve_entity_id(registry, source)):
|
||||
return
|
||||
if (entity_entry := registry.async_get(entity_id)) is None:
|
||||
return
|
||||
if entity_entry.hidden_by != er.RegistryEntryHider.INTEGRATION:
|
||||
return
|
||||
|
||||
# Every inverse as it is now, this one with its new options. A removed
|
||||
# one is already gone from the list by the time it is asked about.
|
||||
for other in hass.config_entries.async_entries(DOMAIN):
|
||||
if other.options.get(CONF_HIDE_SOURCE) and (
|
||||
er.async_resolve_entity_id(registry, other.options[CONF_ENTITY_ID])
|
||||
== entity_id
|
||||
):
|
||||
return
|
||||
|
||||
registry.async_update_entity(entity_id, hidden_by=None)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(
|
||||
@@ -59,19 +138,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Remove a config entry, unhide the source entity."""
|
||||
registry = er.async_get(hass)
|
||||
if not entry.options[CONF_HIDE_SOURCE]:
|
||||
return
|
||||
if not (
|
||||
entity_id := er.async_resolve_entity_id(registry, entry.options[CONF_ENTITY_ID])
|
||||
):
|
||||
return
|
||||
if (entity_entry := registry.async_get(entity_id)) is None:
|
||||
return
|
||||
if entity_entry.hidden_by != er.RegistryEntryHider.INTEGRATION:
|
||||
return
|
||||
|
||||
registry.async_update_entity(entity_id, hidden_by=None)
|
||||
if entry.options[CONF_HIDE_SOURCE]:
|
||||
async_release_source(hass, entry.options[CONF_ENTITY_ID])
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user