Skip to content

Commit

Permalink
hass integration now moved to hass component
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelveldt committed Sep 17, 2020
1 parent b3a3207 commit 0d93486
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 317 deletions.
9 changes: 8 additions & 1 deletion music_assistant/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""All constants for Music Assistant."""

__version__ = "0.0.31"
__version__ = "0.0.32"
REQUIRED_PYTHON_VER = "3.7"

CONF_USERNAME = "username"
Expand Down Expand Up @@ -32,4 +32,11 @@
EVENT_SHUTDOWN = "application shutdown"
EVENT_PROVIDER_REGISTERED = "provider registered"
EVENT_PLAYER_CONTROL_REGISTERED = "player control registered"
EVENT_PLAYER_CONTROL_UNREGISTERED = "player control unregistered"
EVENT_PLAYER_CONTROL_UPDATED = "player control updated"
EVENT_SET_PLAYER_CONTROL_STATE = "set player control state"

# websocket commands
EVENT_REGISTER_PLAYER_CONTROL = "register player control"
EVENT_UNREGISTER_PLAYER_CONTROL = "unregister player control"
EVENT_UPDATE_PLAYER_CONTROL = "update player control"
29 changes: 26 additions & 3 deletions music_assistant/models/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Any, List

from mashumaro import DataClassDictMixin
from music_assistant.constants import EVENT_SET_PLAYER_CONTROL_STATE
from music_assistant.models.config_entry import ConfigEntry
from music_assistant.utils import CustomIntEnum

Expand Down Expand Up @@ -84,7 +85,7 @@ class PlayerControlType(CustomIntEnum):


@dataclass
class PlayerControl(DataClassDictMixin):
class PlayerControl:
"""
Model for a player control.
Expand All @@ -93,7 +94,29 @@ class PlayerControl(DataClassDictMixin):
"""

type: PlayerControlType = PlayerControlType.UNKNOWN
id: str = ""
control_id: str = ""
provider: str = ""
name: str = ""
state: Any = None
set_state: Any = None

async def async_set_state(self, new_state: Any):
"""Handle command to set the state for a player control."""
# by default we just signal an event on the eventbus
# pickup this event (e.g. from the websocket api)
# or override this method with your own implementation.

# pylint: disable=no-member
self.mass.signal_event(
EVENT_SET_PLAYER_CONTROL_STATE,
{"control_id": self.control_id, "state": new_state},
)

def to_dict(self):
"""Return dict representation of this playercontrol."""
return {
"type": int(self.type),
"control_id": self.control_id,
"provider": self.provider,
"name": self.name,
"state": self.state,
}
67 changes: 51 additions & 16 deletions music_assistant/player_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import logging
from datetime import datetime
from typing import Any, List, Optional
from typing import List, Optional

from music_assistant.constants import (
CONF_ENABLED,
Expand All @@ -12,6 +12,8 @@
EVENT_PLAYER_CONTROL_REGISTERED,
EVENT_PLAYER_CONTROL_UPDATED,
EVENT_PLAYER_REMOVED,
EVENT_REGISTER_PLAYER_CONTROL,
EVENT_UNREGISTER_PLAYER_CONTROL,
)
from music_assistant.models.config_entry import ConfigEntry, ConfigEntryType
from music_assistant.models.media_types import MediaItem, MediaType, Track
Expand Down Expand Up @@ -51,6 +53,14 @@ def __init__(self, mass):
self._poll_ticks = 0
self._controls = {}
self._player_controls_config_entries = []
self.mass.add_event_listener(
self.__handle_websocket_player_control_event,
[
EVENT_REGISTER_PLAYER_CONTROL,
EVENT_UNREGISTER_PLAYER_CONTROL,
EVENT_PLAYER_CONTROL_UPDATED,
],
)

async def async_setup(self):
"""Async initialize of module."""
Expand Down Expand Up @@ -162,26 +172,39 @@ async def async_update_player(self, player: Player):

async def async_register_player_control(self, control: PlayerControl):
"""Register a playercontrol with the player manager."""
self._controls[control.id] = control
LOGGER.info("New %s PlayerControl registered: %s", control.type, control.name)
self.mass.signal_event(EVENT_PLAYER_CONTROL_REGISTERED, control.id)
# control.mass = self.mass
control.mass = self.mass
control.type = PlayerControlType(control.type)
self._controls[control.control_id] = control
LOGGER.info(
"New PlayerControl (%s) registered: %s\\%s",
control.type,
control.provider,
control.name,
)
await self.__async_create_playercontrol_config_entries()
# update all players as they may want to use this control
for player in self._players.values():
self.mass.add_job(self.async_update_player(player))

async def async_update_player_control(self, control_id: str, new_state: Any):
async def async_update_player_control(self, control: PlayerControl):
"""Update a playercontrol's state on the player manager."""
control = self._controls.get(control_id)
if not control or control.state == new_state:
if control.control_id not in self._controls:
return await self.async_register_player_control(control)
new_state = control.state
if self._controls[control.control_id].state == new_state:
return
LOGGER.info("PlayerControl %s updated - new state: %s", control.name, new_state)
control.state = new_state
self.mass.signal_event(EVENT_PLAYER_CONTROL_UPDATED, control.id)
self._controls[control.control_id].state = new_state
LOGGER.debug(
"PlayerControl %s\\%s updated - new state: %s",
control.provider,
control.name,
new_state,
)
# update all players using this playercontrol
for player_id, player in self._players.items():
conf = self.mass.config.player_settings[player_id]
if control.id in [
if control.control_id in [
conf.get(CONF_POWER_CONTROL),
conf.get(CONF_VOLUME_CONTROL),
]:
Expand Down Expand Up @@ -361,7 +384,7 @@ async def async_cmd_power_on(self, player_id: str) -> None:
if player_config.get(CONF_POWER_CONTROL):
control = self.get_player_control(player_config[CONF_POWER_CONTROL])
if control:
self.mass.add_job(control.set_state, control.id, True)
await control.async_set_state(True)

async def async_cmd_power_off(self, player_id: str) -> None:
"""
Expand All @@ -377,7 +400,7 @@ async def async_cmd_power_off(self, player_id: str) -> None:
if player_config.get(CONF_POWER_CONTROL):
control = self.get_player_control(player_config[CONF_POWER_CONTROL])
if control:
self.mass.add_job(control.set_state, control.id, False)
await control.async_set_state(False)
# handle group power
if player.is_group_player:
# player is group, turn off all childs
Expand Down Expand Up @@ -433,7 +456,7 @@ async def async_cmd_volume_set(self, player_id: str, volume_level: int) -> None:
if player_config.get(CONF_VOLUME_CONTROL):
control = self.get_player_control(player_config[CONF_VOLUME_CONTROL])
if control:
self.mass.add_job(control.set_state, control.id, volume_level)
await control.async_set_state(volume_level)
# just force full volume on actual player if volume is outsourced to volumecontrol
await player_prov.async_cmd_volume_set(player_id, 100)
# handle group volume
Expand Down Expand Up @@ -645,7 +668,8 @@ async def __async_create_playercontrol_config_entries(self):
power_controls = self.get_player_controls(PlayerControlType.POWER)
if power_controls:
controls = [
{"text": item.name, "value": item.id} for item in power_controls
{"text": f"{item.provider}: {item.name}", "value": item.control_id}
for item in power_controls
]
entries.append(
ConfigEntry(
Expand All @@ -659,7 +683,8 @@ async def __async_create_playercontrol_config_entries(self):
volume_controls = self.get_player_controls(PlayerControlType.VOLUME)
if volume_controls:
controls = [
{"text": item.name, "value": item.id} for item in volume_controls
{"text": f"{item.provider}: {item.name}", "value": item.control_id}
for item in volume_controls
]
entries.append(
ConfigEntry(
Expand Down Expand Up @@ -695,3 +720,13 @@ def __player_updated(self, player_id: str, changed_value: str):
self.mass.add_job(self.async_update_player(child_player))
if player_id in self._player_queues and player.active_queue == player_id:
self.mass.add_job(self._player_queues[player_id].async_update_state())

async def __handle_websocket_player_control_event(self, msg, msg_details):
"""Handle player controls over the websockets api."""
if msg in [EVENT_REGISTER_PLAYER_CONTROL, EVENT_PLAYER_CONTROL_UPDATED]:
# create or update a playercontrol registered through the websockets api
control = PlayerControl(**msg_details)
await self.async_update_player_control(control)
# send confirmation to the client that the register was successful
if msg == EVENT_PLAYER_CONTROL_REGISTERED:
self.mass.signal_event(EVENT_PLAYER_CONTROL_REGISTERED, control)
Loading

0 comments on commit 0d93486

Please sign in to comment.