fix: musiccast - volume level handling + device discovery + current track (#2165)
authorFabian Munkes <105975993+fmunkes@users.noreply.github.com>
Sun, 4 May 2025 18:32:43 +0000 (20:32 +0200)
committerGitHub <noreply@github.com>
Sun, 4 May 2025 18:32:43 +0000 (20:32 +0200)
music_assistant/providers/musiccast/__init__.py

index 47acd9ea7bf4487591ab6445f9cdbbdc294eef9e..28c6e71ca62f5d5acdcf9448d38f7beb4a9f2ff2 100644 (file)
@@ -7,9 +7,12 @@ import logging
 import time
 from collections.abc import Callable, Coroutine
 from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, cast
 
-from aiohttp.client_exceptions import ServerDisconnectedError
+from aiohttp.client_exceptions import (
+    ClientError,
+    ServerDisconnectedError,
+)
 from aiomusiccast.exceptions import MusicCastGroupException
 from aiomusiccast.musiccast_device import MusicCastDevice
 from aiomusiccast.pyamaha import MusicCastConnectionException
@@ -512,10 +515,16 @@ class MusicCast(PlayerProvider):
         device_ip = get_primary_ip_address(info)
         if device_ip is None:
             return
-        device_info = await self.mass.http_session.get(
-            f"http://{device_ip}/{MC_DEVICE_INFO_ENDPOINT}"
-        )
-        if device_info.status == 404:
+        try:
+            device_info = await self.mass.http_session.get(
+                f"http://{device_ip}/{MC_DEVICE_INFO_ENDPOINT}", raise_for_status=True
+            )
+        except ClientError:
+            # typical Errors are
+            # ClientResponseError -> raise_for_status
+            # ClientConnectorError -> unable to connect/ not existing/ timeout
+            # but we can use the base exception class, as we only check
+            # if the device is suitable
             return
         device_info_json = await device_info.json()
         device_id = device_info_json.get("device_id")
@@ -656,9 +665,19 @@ class MusicCast(PlayerProvider):
         player.name = zone_data.name or "UNKNOWN NAME"
         player.powered = zone_data.power == "on"
 
-        player.volume_level = int(
-            zone_data.current_volume / (zone_data.max_volume - zone_data.min_volume) * 100
-        )
+        # NOTE: aiomusiccast does not type hint the volume variables, and they may
+        # be none, and not only integers
+        _current_volume = cast("int | None", zone_data.current_volume)
+        _max_volume = cast("int | None", zone_data.max_volume)
+        _min_volume = cast("int | None", zone_data.min_volume)
+        if _current_volume is None:
+            player.volume_level = None
+        else:
+            _min_volume = 0 if _min_volume is None else _min_volume
+            _max_volume = 100 if _max_volume is None else _max_volume
+            if _min_volume == _max_volume:
+                _max_volume += 1
+            player.volume_level = int(_current_volume / (_max_volume - _min_volume) * 100)
         player.volume_muted = zone_data.mute
 
         # STATE
@@ -733,7 +752,7 @@ class MusicCast(PlayerProvider):
         # and player.set_current_media is the helper function
         # do not access the queue controller to gain playback information here
         if update_helper.current_uri is not None and update_helper.controlled_by_mass:
-            player.set_current_media(uri=update_helper.current_uri)
+            player.set_current_media(uri=update_helper.current_uri, clear_all=True)
         elif device.is_client:
             _server = device.group_server
             _server_id = self._get_player_id_from_mc_zone_player(_server)