ha-core/homeassistant/components/crownstone/listeners.py

154 lines
5.1 KiB
Python
Raw Normal View History

2023-02-03 23:08:48 +01:00
"""Listeners for updating data in the Crownstone integration.
2021-09-14 21:46:52 +02:00
For data updates, Cloud Push is used in form of an SSE server that sends out events.
For fast device switching Local Push is used in form of a USB dongle that hooks into a BLE mesh.
"""
from __future__ import annotations
from functools import partial
from typing import TYPE_CHECKING, cast
from crownstone_cloud.exceptions import CrownstoneNotFoundError
2021-09-14 21:46:52 +02:00
from crownstone_core.packets.serviceDataParsers.containers.AdvExternalCrownstoneState import (
AdvExternalCrownstoneState,
)
from crownstone_core.packets.serviceDataParsers.containers.elements.AdvTypes import (
AdvType,
)
from crownstone_core.protocol.SwitchState import SwitchState
from crownstone_sse.const import (
EVENT_ABILITY_CHANGE,
EVENT_ABILITY_CHANGE_DIMMING,
EVENT_SWITCH_STATE_UPDATE,
)
from crownstone_sse.events import AbilityChangeEvent, SwitchStateUpdateEvent
from crownstone_uart import UartEventBus, UartTopics
from crownstone_uart.topics.SystemTopics import SystemTopics
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
dispatcher_send,
)
2021-09-14 21:46:52 +02:00
from .const import (
DOMAIN,
SIG_CROWNSTONE_STATE_UPDATE,
SIG_UART_STATE_CHANGE,
SSE_LISTENERS,
UART_LISTENERS,
)
if TYPE_CHECKING:
from .entry_manager import CrownstoneEntryManager
@callback
def async_update_crwn_state_sse(
manager: CrownstoneEntryManager, switch_event: SwitchStateUpdateEvent
2021-09-14 21:46:52 +02:00
) -> None:
"""Update the state of a Crownstone when switched externally."""
try:
updated_crownstone = manager.cloud.get_crownstone_by_id(switch_event.cloud_id)
except CrownstoneNotFoundError:
2021-09-14 21:46:52 +02:00
return
# only update on change.
if updated_crownstone.state != switch_event.switch_state:
updated_crownstone.state = switch_event.switch_state
async_dispatcher_send(manager.hass, SIG_CROWNSTONE_STATE_UPDATE)
@callback
def async_update_crwn_ability(
manager: CrownstoneEntryManager, ability_event: AbilityChangeEvent
) -> None:
2021-09-14 21:46:52 +02:00
"""Update the ability information of a Crownstone."""
try:
updated_crownstone = manager.cloud.get_crownstone_by_id(ability_event.cloud_id)
except CrownstoneNotFoundError:
2021-09-14 21:46:52 +02:00
return
ability_type = ability_event.ability_type
ability_enabled = ability_event.ability_enabled
# only update on a change in state
if updated_crownstone.abilities[ability_type].is_enabled == ability_enabled:
return
# write the change to the crownstone entity.
updated_crownstone.abilities[ability_type].is_enabled = ability_enabled
if ability_event.sub_type == EVENT_ABILITY_CHANGE_DIMMING:
# reload the config entry because dimming is part of supported features
manager.hass.async_create_task(
manager.hass.config_entries.async_reload(manager.config_entry.entry_id)
)
else:
async_dispatcher_send(manager.hass, SIG_CROWNSTONE_STATE_UPDATE)
def update_uart_state(manager: CrownstoneEntryManager, _: bool | None) -> None:
"""Update the uart ready state for entities that use USB."""
# update availability of power usage entities.
dispatcher_send(manager.hass, SIG_UART_STATE_CHANGE)
def update_crwn_state_uart(
manager: CrownstoneEntryManager, data: AdvExternalCrownstoneState
) -> None:
"""Update the state of a Crownstone when switched externally."""
if data.type != AdvType.EXTERNAL_STATE:
return
try:
updated_crownstone = manager.cloud.get_crownstone_by_uid(
data.crownstoneId, manager.usb_sphere_id
)
except CrownstoneNotFoundError:
2021-09-14 21:46:52 +02:00
return
if data.switchState is None:
return
# update on change
updated_state = cast(SwitchState, data.switchState)
if updated_crownstone.state != updated_state.intensity:
updated_crownstone.state = updated_state.intensity
dispatcher_send(manager.hass, SIG_CROWNSTONE_STATE_UPDATE)
def setup_sse_listeners(manager: CrownstoneEntryManager) -> None:
"""Set up SSE listeners."""
# save unsub function for when entry removed
manager.listeners[SSE_LISTENERS] = [
async_dispatcher_connect(
manager.hass,
2021-09-14 21:46:52 +02:00
f"{DOMAIN}_{EVENT_SWITCH_STATE_UPDATE}",
partial(async_update_crwn_state_sse, manager),
),
async_dispatcher_connect(
manager.hass,
2021-09-14 21:46:52 +02:00
f"{DOMAIN}_{EVENT_ABILITY_CHANGE}",
partial(async_update_crwn_ability, manager),
),
]
def setup_uart_listeners(manager: CrownstoneEntryManager) -> None:
"""Set up UART listeners."""
# save subscription id to unsub
manager.listeners[UART_LISTENERS] = [
UartEventBus.subscribe(
SystemTopics.connectionEstablished,
partial(update_uart_state, manager),
),
UartEventBus.subscribe(
SystemTopics.connectionClosed,
partial(update_uart_state, manager),
),
UartEventBus.subscribe(
UartTopics.newDataAvailable,
partial(update_crwn_state_uart, manager),
),
]