Home Assistant Unofficial Reference 2024.12.1
media_player.py
Go to the documentation of this file.
1 """Support for interacting with and controlling the cmus music player."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from pycmus import exceptions, remote
9 import voluptuous as vol
10 
12  PLATFORM_SCHEMA as MEDIA_PLAYER_PLATFORM_SCHEMA,
13  MediaPlayerEntity,
14  MediaPlayerEntityFeature,
15  MediaPlayerState,
16  MediaType,
17 )
18 from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT
19 from homeassistant.core import HomeAssistant
21 from homeassistant.helpers.entity_platform import AddEntitiesCallback
22 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
23 
24 _LOGGER = logging.getLogger(__name__)
25 
26 DEFAULT_NAME = "cmus"
27 DEFAULT_PORT = 3000
28 
29 PLATFORM_SCHEMA = MEDIA_PLAYER_PLATFORM_SCHEMA.extend(
30  {
31  vol.Inclusive(CONF_HOST, "remote"): cv.string,
32  vol.Inclusive(CONF_PASSWORD, "remote"): cv.string,
33  vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
34  vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
35  }
36 )
37 
38 
40  hass: HomeAssistant,
41  config: ConfigType,
42  add_entities: AddEntitiesCallback,
43  discover_info: DiscoveryInfoType | None = None,
44 ) -> None:
45  """Set up the CMUS platform."""
46 
47  host = config.get(CONF_HOST)
48  password = config.get(CONF_PASSWORD)
49  port = config[CONF_PORT]
50  name = config[CONF_NAME]
51 
52  cmus_remote = CmusRemote(server=host, port=port, password=password)
53  cmus_remote.connect()
54 
55  if cmus_remote.cmus is None:
56  return
57 
58  add_entities([CmusDevice(device=cmus_remote, name=name, server=host)], True)
59 
60 
61 class CmusRemote:
62  """Representation of a cmus connection."""
63 
64  def __init__(self, server, port, password):
65  """Initialize the cmus remote."""
66 
67  self._server_server = server
68  self._port_port = port
69  self._password_password = password
70  self.cmuscmus = None
71 
72  def connect(self):
73  """Connect to the cmus server."""
74 
75  try:
76  self.cmuscmus = remote.PyCmus(
77  server=self._server_server, port=self._port_port, password=self._password_password
78  )
79  except exceptions.InvalidPassword:
80  _LOGGER.error("The provided password was rejected by cmus")
81 
82 
84  """Representation of a running cmus."""
85 
86  _attr_media_content_type = MediaType.MUSIC
87  _attr_supported_features = (
88  MediaPlayerEntityFeature.PAUSE
89  | MediaPlayerEntityFeature.VOLUME_SET
90  | MediaPlayerEntityFeature.TURN_OFF
91  | MediaPlayerEntityFeature.TURN_ON
92  | MediaPlayerEntityFeature.PREVIOUS_TRACK
93  | MediaPlayerEntityFeature.NEXT_TRACK
94  | MediaPlayerEntityFeature.PLAY_MEDIA
95  | MediaPlayerEntityFeature.SEEK
96  | MediaPlayerEntityFeature.PLAY
97  )
98 
99  def __init__(self, device, name, server):
100  """Initialize the CMUS device."""
101 
102  self._remote_remote = device
103  if server:
104  auto_name = f"cmus-{server}"
105  else:
106  auto_name = "cmus-local"
107  self._attr_name_attr_name = name or auto_name
108  self.statusstatus = {}
109 
110  def update(self) -> None:
111  """Get the latest data and update the state."""
112  try:
113  status = self._remote_remote.cmus.get_status_dict()
114  except BrokenPipeError:
115  self._remote_remote.connect()
116  except exceptions.ConfigurationError:
117  _LOGGER.warning("A configuration error occurred")
118  self._remote_remote.connect()
119  else:
120  self.statusstatus = status
121  if self.statusstatus.get("status") == "playing":
122  self._attr_state_attr_state = MediaPlayerState.PLAYING
123  elif self.statusstatus.get("status") == "paused":
124  self._attr_state_attr_state = MediaPlayerState.PAUSED
125  else:
126  self._attr_state_attr_state = MediaPlayerState.OFF
127  self._attr_media_content_id_attr_media_content_id = self.statusstatus.get("file")
128  self._attr_media_duration_attr_media_duration = self.statusstatus.get("duration")
129  self._attr_media_title_attr_media_title = self.statusstatus["tag"].get("title")
130  self._attr_media_artist_attr_media_artist = self.statusstatus["tag"].get("artist")
131  self._attr_media_track_attr_media_track = self.statusstatus["tag"].get("tracknumber")
132  self._attr_media_album_name_attr_media_album_name = self.statusstatus["tag"].get("album")
133  self._attr_media_album_artist_attr_media_album_artist = self.statusstatus["tag"].get("albumartist")
134  left = self.statusstatus["set"].get("vol_left")[0]
135  right = self.statusstatus["set"].get("vol_right")[0]
136  if left != right:
137  volume = float(left + right) / 2
138  else:
139  volume = left
140  self._attr_volume_level_attr_volume_level = int(volume) / 100
141  return
142 
143  _LOGGER.warning("Received no status from cmus")
144 
145  def turn_off(self) -> None:
146  """Service to send the CMUS the command to stop playing."""
147  self._remote_remote.cmus.player_stop()
148 
149  def turn_on(self) -> None:
150  """Service to send the CMUS the command to start playing."""
151  self._remote_remote.cmus.player_play()
152 
153  def set_volume_level(self, volume: float) -> None:
154  """Set volume level, range 0..1."""
155  self._remote_remote.cmus.set_volume(int(volume * 100))
156 
157  def volume_up(self) -> None:
158  """Set the volume up."""
159  left = self.statusstatus["set"].get("vol_left")
160  right = self.statusstatus["set"].get("vol_right")
161  if left != right:
162  current_volume = float(left + right) / 2
163  else:
164  current_volume = left
165 
166  if current_volume <= 100:
167  self._remote_remote.cmus.set_volume(int(current_volume) + 5)
168 
169  def volume_down(self) -> None:
170  """Set the volume down."""
171  left = self.statusstatus["set"].get("vol_left")
172  right = self.statusstatus["set"].get("vol_right")
173  if left != right:
174  current_volume = float(left + right) / 2
175  else:
176  current_volume = left
177 
178  if current_volume <= 100:
179  self._remote_remote.cmus.set_volume(int(current_volume) - 5)
180 
182  self, media_type: MediaType | str, media_id: str, **kwargs: Any
183  ) -> None:
184  """Send the play command."""
185  if media_type in {MediaType.MUSIC, MediaType.PLAYLIST}:
186  self._remote_remote.cmus.player_play_file(media_id)
187  else:
188  _LOGGER.error(
189  "Invalid media type %s. Only %s and %s are supported",
190  media_type,
191  MediaType.MUSIC,
192  MediaType.PLAYLIST,
193  )
194 
195  def media_pause(self) -> None:
196  """Send the pause command."""
197  self._remote_remote.cmus.player_pause()
198 
199  def media_next_track(self) -> None:
200  """Send next track command."""
201  self._remote_remote.cmus.player_next()
202 
203  def media_previous_track(self) -> None:
204  """Send next track command."""
205  self._remote_remote.cmus.player_prev()
206 
207  def media_seek(self, position: float) -> None:
208  """Send seek command."""
209  self._remote_remote.cmus.seek(position)
210 
211  def media_play(self) -> None:
212  """Send the play command."""
213  self._remote_remote.cmus.player_play()
214 
215  def media_stop(self) -> None:
216  """Send the stop command."""
217  self._remote_remote.cmus.stop()
None play_media(self, MediaType|str media_type, str media_id, **Any kwargs)
def __init__(self, server, port, password)
Definition: media_player.py:64
None add_entities(AsusWrtRouter router, AddEntitiesCallback async_add_entities, set[str] tracked)
None setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback add_entities, DiscoveryInfoType|None discover_info=None)
Definition: media_player.py:44
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88