Skip to content

Commit

Permalink
Merge pull request #6 from rumbu13:rumbu13/issue4
Browse files Browse the repository at this point in the history
Event data for mediabrowser_library_changed
  • Loading branch information
rumbu13 committed Jun 1, 2023
2 parents 0096c98 + 24ddaa2 commit b67b17f
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 22 deletions.
18 changes: 9 additions & 9 deletions custom_components/mediabrowser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import asyncio
import logging
from typing import Any

import aiohttp
from homeassistant.config_entries import (
Expand All @@ -12,9 +11,9 @@
ConfigEntryNotReady,
)
from homeassistant.const import CONF_URL, Platform
from homeassistant.core import HomeAssistant, Context
from homeassistant.core import HomeAssistant

from .helpers import snake_cased_json
from .helpers import size_of, snake_cased_json

from .const import (
CONF_CACHE_SERVER_API_KEY,
Expand All @@ -23,12 +22,7 @@
CONF_CACHE_SERVER_PING,
CONF_CACHE_SERVER_USER_ID,
CONF_CACHE_SERVER_VERSION,
CONF_SENSOR_ITEM_TYPE,
CONF_SENSOR_LIBRARY,
CONF_SENSOR_USER,
CONF_SENSORS,
DATA_HUB,
DATA_POLL_COORDINATOR,
DOMAIN,
)
from .hub import MediaBrowserHub
Expand All @@ -46,7 +40,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_websocket_message(
message_type: str, data: dict[str, None] | None
) -> None:
_LOGGER.debug("%s firing event %s_%s", hub.server_name, DOMAIN, message_type)
_LOGGER.debug(
"%s firing event %s_%s (%d bytes)",
hub.server_name,
DOMAIN,
message_type,
size_of(data),
)
hass.bus.async_fire(
f"{DOMAIN}_{message_type}",
snake_cased_json(data),
Expand Down
32 changes: 32 additions & 0 deletions custom_components/mediabrowser/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ class Item(StrEnum):
MBCLIENT = "MediaBrowserClient"
MEDIA_SOURCE_ID = "MediaSourceId"
MEDIA_SOURCES = "MediaSources"
MEDIA_STREAMS = "MediaStreams"
MEDIA_TYPE = "MediaType"
MESSAGE_TYPE = "MessageType"
NAME = "Name"
Expand Down Expand Up @@ -362,6 +363,36 @@ class VirtualFolder(StrEnum):
YEARS = "years"


class UserDataChange(StrEnum):
"""Keys for UserDataChanged event"""

USER_ID = "UserId"
USER_DATA_LIST = "UserDataList"


class LibraryChange(StrEnum):
"""Keys for LibraryChange event"""

ITEMS_ADDED = "ItemsAdded"
ITEMS_UPDATED = "ItemsUpdated"
ITEMS_REMOVED = "ItemsRemoved"
FOLDERS_ADDED_TO = "FoldersAddedTo"
FOLDERS_REMOVED_FROM = "FoldersRemovedFrom"
COLLECTION_FOLDERS = "CollectionFolders"


class WebsocketMessage(StrEnum):
"""Websocket message types"""

ACTIVITY_LOG_ENTRY = "ActivityLogEntry"
FORCE_KEEP_ALIVE = "ForceKeepAlive"
KEEP_ALIVE = "KeepAlive"
LIBRARY_CHANGED = "LibraryChanged"
SCHEDULED_TASK_INFO = "ScheduledTaskInfo"
SESSIONS = "Sessions"
USER_DATA_CHANGED = "UserDataChanged"


class ImageType(StrEnum):
"""Image types."""

Expand Down Expand Up @@ -454,6 +485,7 @@ class Session(StrEnum):
PLAYLIST_INDEX = "PlaylistIndex"
PLAYLIST_LENGTH = "PlaylistLength"
REMOTE_END_POINT = "RemoteEndPoint"
SERVER_ID = "ServerId"
SUPPORTS_REMOTE_CONTROL = "SupportsRemoteControl"
SUPPORTED_COMMANDS = "SupportedCommands"
USER_NAME = "UserName"
Expand Down
88 changes: 87 additions & 1 deletion custom_components/mediabrowser/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime
import logging
import re
from sys import getsizeof
from typing import Any

from dateutil import parser
Expand All @@ -18,6 +19,9 @@
ImageType,
ItemType,
Item,
LibraryChange,
Session,
UserDataChange,
)

_LOGGER = logging.getLogger(__package__)
Expand Down Expand Up @@ -213,8 +217,90 @@ def camel_cased_json(original: Any | None) -> Any | None:


def autolog(message):
"Automatically log the current function details."
"""Automatically log the current function details."""
func = inspect.currentframe().f_back.f_code # type: ignore
_LOGGER.debug(
"%s: %s in %s:%i", message, func.co_name, func.co_filename, func.co_firstlineno
)


def get_session_event_data(session: dict[str, Any]) -> dict[str, Any]:
"""Translate session information in event data"""
result: dict[str, Any] = {}

for key in [
Session.REMOTE_END_POINT,
Session.ID,
Session.CLIENT,
Session.LAST_ACTIVITY_DATE,
Session.SERVER_ID,
Session.DEVICE_NAME,
Session.APPLICATION_VERSION,
Session.PLAY_STATE,
Session.APP_ICON_URL,
Session.SUPPORTS_REMOTE_CONTROL,
]:
if field := session.get(key):
result[key] = field

if play_state := session.get(Session.PLAY_STATE):
result |= play_state

if npi := session.get(Session.NOW_PLAYING_ITEM):
for key in [
Item.NAME,
Item.ID,
Item.PARENT_ID,
Item.PATH,
Item.RUNTIME_TICKS,
Item.TYPE,
Item.MEDIA_TYPE,
]:
if field := npi.get(key):
result[f"NowPlaying{key}"] = field
return result


def get_user_data_changed_event_data(event: dict[str, Any]) -> dict[str, Any]:
"""Translate user data changed notification in event data"""
result = {}
if user_id := event.get(UserDataChange.USER_ID):
result[UserDataChange.USER_ID] = user_id
if data_list := event.get(UserDataChange.USER_DATA_LIST):
if len(data_list) > 5:
result[UserDataChange.USER_DATA_LIST] = data_list[:5]
else:
result[UserDataChange.USER_DATA_LIST] = data_list

return result


def get_library_changed_event_data(event: dict[str, Any]) -> dict[str, Any]:
"""Translate user data changed notification in event data"""
result = {}
for key in [
LibraryChange.FOLDERS_ADDED_TO,
LibraryChange.ITEMS_ADDED,
LibraryChange.ITEMS_REMOVED,
LibraryChange.ITEMS_UPDATED,
LibraryChange.FOLDERS_REMOVED_FROM,
]:
if data := event.get(key):
if len(data) > 5:
result[key] = data[:5]
else:
result[key] = data

return result


def size_of(data: Any):
"""Returns the approximate memory footprint an object and all of its contents."""

result = getsizeof(data)
if isinstance(data, list):
result += sum((size_of(item) for item in data))
elif isinstance(data, dict):
result += sum((size_of(key) + size_of(item) for key, item in data.items()))

return result
36 changes: 24 additions & 12 deletions custom_components/mediabrowser/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
from homeassistant.const import CONF_USERNAME, CONF_NAME, CONF_PASSWORD, CONF_URL
from homeassistant.util import uuid

from .helpers import snake_case
from .helpers import (
get_library_changed_event_data,
get_user_data_changed_event_data,
get_session_event_data,
snake_case,
)

from .const import (
APP_PLAYERS,
Expand Down Expand Up @@ -65,6 +70,7 @@
ServerType,
Session,
Value,
WebsocketMessage,
)


Expand Down Expand Up @@ -786,7 +792,10 @@ async def _handle_sessions_message(self, sessions: list[dict[str, Any]]) -> None
old_raw_sessions = deepcopy(self._raw_sessions)
old_sessions = deepcopy(self._sessions)

new_raw_sessions = {session["Id"]: session for session in sessions}
new_raw_sessions = {
session["Id"]: get_session_event_data(deepcopy(session))
for session in sessions
}
new_sessions = {
session["Id"]: session for session in self._preprocess_sessions(sessions)
}
Expand Down Expand Up @@ -918,39 +927,42 @@ async def _handle_message(self, message: str):

if msg_type := msg.get("MessageType"):
call_listeners = self.send_other_events
data = msg.get("Data")
match msg_type:
case "Sessions":
sessions = deepcopy(msg["Data"])
case WebsocketMessage.SESSIONS:
sessions = deepcopy(data)
asyncio.ensure_future(self._handle_sessions_message(sessions))
call_listeners = False
case "KeepAlive":
case WebsocketMessage.KEEP_ALIVE:
_LOGGER.debug(
"KeepAlive response received from %s", self.server_url
)
call_listeners = False
case "ForceKeepAlive":
case WebsocketMessage.FORCE_KEEP_ALIVE:
_LOGGER.debug(
"ForceKeepAlive response received from %s", self.server_url
)
self._keep_alive_timeout = msg.get("Data", KEEP_ALIVE_TIMEOUT) / 2
call_listeners = False
case "LibraryChanged":
event = msg["Data"]
case WebsocketMessage.LIBRARY_CHANGED:
if any(self._library_listeners):
asyncio.ensure_future(
self._handle_library_changed_message(event)
self._handle_library_changed_message(data)
)
case "ActivityLogEntry":
data = get_library_changed_event_data(data)
case WebsocketMessage.ACTIVITY_LOG_ENTRY:
if self.send_activity_events and any(self._websocket_listeners):
asyncio.ensure_future(self._handle_activity_log_message())
call_listeners = False
case "ScheduledTasksInfo":
case WebsocketMessage.SCHEDULED_TASK_INFO:
call_listeners = self.send_task_events
case WebsocketMessage.USER_DATA_CHANGED:
data = get_user_data_changed_event_data(data)

if call_listeners and any(self._websocket_listeners):
data = {
"server_id": self.server_id,
snake_case(msg_type): deepcopy(msg.get("Data", {})),
snake_case(msg_type): (data or {}),
}
asyncio.ensure_future(
self._call_websocket_listeners(snake_case(msg_type), data)
Expand Down

0 comments on commit b67b17f

Please sign in to comment.