Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
aneisch committed Aug 30, 2024
1 parent 0cf0100 commit e965db4
Show file tree
Hide file tree
Showing 2,010 changed files with 5,949 additions and 1,422 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Home Assistant and other containers have ingress handled automatically by [Traef
Description | value
-- | --
Lines of ESPHome YAML | 3254
Lines of Home Assistant YAML | 9668
Lines of Home Assistant YAML | 9685
[Integrations](https://www.home-assistant.io/integrations/) in use | 62
Zigbee devices in [`zha`](https://www.home-assistant.io/integrations/zha/) | 26
Z-Wave devices in [`zwave_js`](https://www.home-assistant.io/integrations/zwave_js/) | 37
Expand Down Expand Up @@ -95,18 +95,18 @@ Entities in the [`plant`](https://www.home-assistant.io/components/plant) domain
Entities in the [`remote`](https://www.home-assistant.io/components/remote) domain | 4
Entities in the [`script`](https://www.home-assistant.io/components/script) domain | 51
Entities in the [`select`](https://www.home-assistant.io/components/select) domain | 4
Entities in the [`sensor`](https://www.home-assistant.io/components/sensor) domain | 524
Entities in the [`sensor`](https://www.home-assistant.io/components/sensor) domain | 523
Entities in the [`setter`](https://www.home-assistant.io/components/setter) domain | 1
Entities in the [`siren`](https://www.home-assistant.io/components/siren) domain | 1
Entities in the [`sun`](https://www.home-assistant.io/components/sun) domain | 1
Entities in the [`switch`](https://www.home-assistant.io/components/switch) domain | 181
Entities in the [`switch`](https://www.home-assistant.io/components/switch) domain | 186
Entities in the [`timer`](https://www.home-assistant.io/components/timer) domain | 6
Entities in the [`tts`](https://www.home-assistant.io/components/tts) domain | 1
Entities in the [`update`](https://www.home-assistant.io/components/update) domain | 35
Entities in the [`update`](https://www.home-assistant.io/components/update) domain | 81
Entities in the [`vacuum`](https://www.home-assistant.io/components/vacuum) domain | 1
Entities in the [`weather`](https://www.home-assistant.io/components/weather) domain | 1
Entities in the [`zone`](https://www.home-assistant.io/components/zone) domain | 6
**Total state objects** | **1425**
**Total state objects** | **1475**
## The HACS integrations/plugins that I use:

**Appdaemon**:<br>
Expand Down
14 changes: 12 additions & 2 deletions custom_components/alexa_media/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1243,8 +1243,18 @@ async def http2_error_handler(message):
_LOGGER.debug("Setting up Alexa devices for %s", hide_email(login_obj.email))
config = config_entry.data
email = config.get(CONF_EMAIL)
include = config.get(CONF_INCLUDE_DEVICES)
exclude = config.get(CONF_EXCLUDE_DEVICES)
include = (
cv.ensure_list_csv(config[CONF_INCLUDE_DEVICES])
if config[CONF_INCLUDE_DEVICES]
else ""
)
_LOGGER.debug("include: %s", include)
exclude = (
cv.ensure_list_csv(config[CONF_EXCLUDE_DEVICES])
if config[CONF_EXCLUDE_DEVICES]
else ""
)
_LOGGER.debug("exclude: %s", exclude)
scan_interval: float = (
config.get(CONF_SCAN_INTERVAL).total_seconds()
if isinstance(config.get(CONF_SCAN_INTERVAL), timedelta)
Expand Down
7 changes: 5 additions & 2 deletions custom_components/alexa_media/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
)
from homeassistant import config_entries
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.components.persistent_notification import (
async_dismiss as async_dismiss_persistent_notification,
)
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, CONF_SCAN_INTERVAL, CONF_URL
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult, UnknownFlow
Expand Down Expand Up @@ -642,7 +645,7 @@ async def _test_login(self):
"alexa_media_relogin_success",
event_data={"email": hide_email(email), "url": login.url},
)
self.hass.components.persistent_notification.async_dismiss(
async_dismiss_persistent_notification(
f"alexa_media_{slugify(email)}{slugify(login.url[7:])}"
)
if not self.hass.data[DATA_ALEXAMEDIA]["accounts"].get(
Expand Down Expand Up @@ -695,7 +698,7 @@ async def _test_login(self):
if login.status and (login.status.get("login_failed")):
_LOGGER.debug("Login failed: %s", login.status.get("login_failed"))
await login.close()
self.hass.components.persistent_notification.async_dismiss(
async_dismiss_persistent_notification(
f"alexa_media_{slugify(email)}{slugify(login.url[7:])}"
)
return self.async_abort(reason="login_failed")
Expand Down
2 changes: 1 addition & 1 deletion custom_components/alexa_media/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
PERCENTAGE,
)

__version__ = "4.12.9"
__version__ = "4.12.11"
PROJECT_URL = "https://github.com/alandtse/alexa_media_player/"
ISSUE_URL = f"{PROJECT_URL}issues"
NOTIFY_URL = f"{PROJECT_URL}wiki/Configuration%3A-Notification-Component#use-the-notifyalexa_media-service"
Expand Down
2 changes: 1 addition & 1 deletion custom_components/alexa_media/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"issue_tracker": "https://github.com/alandtse/alexa_media_player/issues",
"loggers": ["alexapy", "authcaptureproxy"],
"requirements": ["alexapy==1.28.2", "packaging>=20.3", "wrapt>=1.14.0"],
"version": "4.12.9"
"version": "4.12.11"
}
6 changes: 6 additions & 0 deletions custom_components/frigate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
ATTR_CLIENT,
ATTR_CONFIG,
ATTR_COORDINATOR,
ATTR_WS_EVENT_PROXY,
ATTRIBUTE_LABELS,
CONF_CAMERA_STATIC_IMAGE_HEIGHT,
DOMAIN,
Expand All @@ -52,6 +53,7 @@
)
from .views import async_setup as views_async_setup
from .ws_api import async_setup as ws_api_async_setup
from .ws_event_proxy import WSEventProxy

SCAN_INTERVAL = timedelta(seconds=5)

Expand Down Expand Up @@ -215,11 +217,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

model = f"{(await async_get_integration(hass, DOMAIN)).version}/{server_version}"

ws_event_proxy = WSEventProxy(config["mqtt"]["topic_prefix"])
entry.async_on_unload(lambda: ws_event_proxy.unsubscribe_all(hass))

hass.data[DOMAIN][entry.entry_id] = {
ATTR_COORDINATOR: coordinator,
ATTR_CLIENT: client,
ATTR_CONFIG: config,
ATTR_MODEL: model,
ATTR_WS_EVENT_PROXY: ws_event_proxy,
}

# Remove old devices associated with cameras that have since been removed
Expand Down
1 change: 1 addition & 0 deletions custom_components/frigate/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
ATTR_PTZ_ACTION = "action"
ATTR_PTZ_ARGUMENT = "argument"
ATTR_START_TIME = "start_time"
ATTR_WS_EVENT_PROXY = "ws_event_proxy"

# Frigate Attribute Labels
# These are labels that are not individually tracked as they are
Expand Down
2 changes: 1 addition & 1 deletion custom_components/frigate/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/blakeblackshear/frigate-hass-integration/issues",
"requirements": ["pytz"],
"version": "5.3.0"
"version": "5.4.0"
}
74 changes: 72 additions & 2 deletions custom_components/frigate/ws_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
import voluptuous as vol

from custom_components.frigate.api import FrigateApiClient, FrigateApiClientError
from custom_components.frigate.views import get_client_for_frigate_instance_id
from custom_components.frigate.const import ATTR_WS_EVENT_PROXY, DOMAIN
from custom_components.frigate.views import (
get_client_for_frigate_instance_id,
get_config_entry_for_frigate_instance_id,
)
from custom_components.frigate.ws_event_proxy import WSEventProxy
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant

Expand All @@ -21,6 +26,8 @@ def async_setup(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, ws_get_events)
websocket_api.async_register_command(hass, ws_get_events_summary)
websocket_api.async_register_command(hass, ws_get_ptz_info)
websocket_api.async_register_command(hass, ws_subscribe_events)
websocket_api.async_register_command(hass, ws_unsubscribe_events)


def _get_client_or_send_error(
Expand Down Expand Up @@ -157,7 +164,6 @@ async def ws_get_recordings_summary(
vol.Optional("limit"): int,
vol.Optional("has_clip"): bool,
vol.Optional("has_snapshot"): bool,
vol.Optional("has_snapshot"): bool,
vol.Optional("favorites"): bool,
}
) # type: ignore[misc]
Expand Down Expand Up @@ -237,6 +243,70 @@ async def ws_get_events_summary(
)


@websocket_api.websocket_command(
{
vol.Required("type"): "frigate/events/subscribe",
vol.Required("instance_id"): str,
}
) # type: ignore[misc]
@websocket_api.async_response # type: ignore[misc]
async def ws_subscribe_events(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict,
) -> None:
"""Subscribe to events."""

entry = get_config_entry_for_frigate_instance_id(hass, msg["instance_id"])
if not entry:
connection.send_error(
msg["id"],
"not_found",
f"API error whilst subscribing to events for unknown Frigate instance "
f"{msg['instance_id']}",
)
return

event_proxy: WSEventProxy = hass.data[DOMAIN][entry.entry_id][ATTR_WS_EVENT_PROXY]
connection.send_result(
msg["id"], await event_proxy.subscribe(hass, msg["id"], connection)
)


@websocket_api.websocket_command(
{
vol.Required("type"): "frigate/events/unsubscribe",
vol.Required("instance_id"): str,
vol.Required("subscription_id"): int,
}
) # type: ignore[misc]
@websocket_api.async_response # type: ignore[misc]
async def ws_unsubscribe_events(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict,
) -> None:
"""Unsubscribe from events."""

entry = get_config_entry_for_frigate_instance_id(hass, msg["instance_id"])
if not entry:
connection.send_error(
msg["id"],
"not_found",
f"API error whilst unsubscribing to events for unknown Frigate instance "
f"{msg['instance_id']}",
)
return

event_proxy: WSEventProxy = hass.data[DOMAIN][entry.entry_id][ATTR_WS_EVENT_PROXY]
if event_proxy.unsubscribe(hass, msg["subscription_id"]):
connection.send_result(msg["id"])
else:
connection.send_error(
msg["id"], websocket_api.const.ERR_NOT_FOUND, "Subscription not found."
)


@websocket_api.websocket_command(
{
vol.Required("type"): "frigate/ptz/info",
Expand Down
95 changes: 95 additions & 0 deletions custom_components/frigate/ws_event_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Frigate event proxy."""
from __future__ import annotations

import logging

from homeassistant.components import websocket_api
from homeassistant.components.mqtt.models import ReceiveMessage
from homeassistant.components.mqtt.subscription import (
async_prepare_subscribe_topics,
async_subscribe_topics,
async_unsubscribe_topics,
)
from homeassistant.components.websocket_api import messages
from homeassistant.core import HomeAssistant

_LOGGER: logging.Logger = logging.getLogger(__name__)


class WSEventProxy:
"""Frigate event MQTT to WS proxy.
This class subscribes to the MQTT events topic for a given Frigate topic and
forwards the messages to a list of subscribers. MQTT payload is directly
passed to subscribers to avoid JSON serialization/deserialization overhead
within HA.
"""

def __init__(self, topic_prefix: str) -> None:
self._subscriptions: dict[int, websocket_api.ActiveConnection] = {}
self._topics = {
"events": {
"topic": f"{topic_prefix}/events",
"msg_callback": lambda msg: self._receive_message(msg),
"qos": 0,
}
}
self._sub_state = None

async def subscribe(
self,
hass: HomeAssistant,
subscription_id: int,
connection: websocket_api.ActiveConnection,
) -> int:
"""Subscribe to events."""

if self._sub_state is None:
self._sub_state = async_prepare_subscribe_topics(
hass, self._sub_state, self._topics
)
await async_subscribe_topics(hass, self._sub_state)

# Add a callback to the websocket to unsubscribe if closed.
connection.subscriptions[subscription_id] = lambda: self._unsubscribe_internal(
hass, subscription_id
)
self._subscriptions[subscription_id] = connection
return subscription_id

def unsubscribe(self, hass: HomeAssistant, subscription_id: int) -> bool:
"""Unsubscribe from events."""

if (
subscription_id in self._subscriptions
and subscription_id in self._subscriptions[subscription_id].subscriptions
):
self._subscriptions[subscription_id].subscriptions.pop(subscription_id)
return self._unsubscribe_internal(hass, subscription_id)

def _unsubscribe_internal(self, hass: HomeAssistant, subscription_id: int) -> bool:
"""Unsubscribe from events.
May be called from the websocket connection close handler. As a result
must not change the size of connection.subscriptions which is iterated
over in that handler.
"""

if subscription_id not in self._subscriptions:
return False
self._subscriptions.pop(subscription_id)

if not self._subscriptions:
async_unsubscribe_topics(hass, self._sub_state)
self._sub_state = None
return True

def unsubscribe_all(self, hass: HomeAssistant) -> None:
"""Unsubscribe all subscribers."""
for subscription_id in list(self._subscriptions.keys()):
self.unsubscribe(hass, subscription_id)

def _receive_message(self, msg: ReceiveMessage) -> None:
"""Handle a new received MQTT message."""
for id, connection in self._subscriptions.items():
connection.send_message(messages.event_message(id, msg.payload))
Loading

0 comments on commit e965db4

Please sign in to comment.