Closed c-mellueh closed 2 years ago
Are you using Spotify in SingleRoom
mode or in MultiRoom
mode`.
PS: I cannot test Spotify and therefore the integration works with Spotify only to that extend it compatible to the way it is working with Tidal. Some modifications have been made to better support Spotify but these were completely driven by user feedback - what makes development on my end a bit challenging.
Hi, i am using Spotify in SingleRoom mode. I allready noticed your Spotify "problem". I would like to help but do not fully understand how UPNP and DLNA are working. If you have some sources that i can read to understand the underlying tech a bit more i would be really happy.
As far as i understand there seams to be some problems with RuntimeErrors:
If I try
>>> host = "xxx.xxx.xxx.xx"
>>> import hassfeld
>>> rf = hassfeld.RaumfeldHost(host)
>>> rf.start_update_thread()
>>> rf.get_rooms()
['Bad', 'Schlafzimmer', 'Wohnzimmer', 'Kueche']
>>> rf.get_zones()
[]
>>> rf.get_room_volume("Schlafzimmer")
I get:
Unexpected error with async_get_volume: <class 'RuntimeError'>Timeout context manager should be used inside a task
The current implementation of asyncio in hassfeld
breaks the non-asyncio usage: https://github.com/B5r1oJ0A9G/hassfeld/issues/3.
I was not able yet to find a suitable implementation that works for both scenarios - asyncio and non-asyncio.
So, to use hassfeld
you have to do it currently in an asynchronous manner:
import asyncio
import aiohttp
import hassfeld
async def main():
host = "teufel-host.example.com"
port = 47365
session = aiohttp.ClientSession()
raumfeld = hassfeld.RaumfeldHost(host, port, session=session)
asyncio.create_task(raumfeld.async_update_all(session))
await raumfeld.async_wait_initial_update()
zone = ["Master Bedroom"]
media_info = await raumfeld.async_get_media_info(zone)
print(f"Media info: {media_info}")
await session.close()
asyncio.run(main())
In regards to informational sources concernign UPnP and more specifically DLNA I don't have the links in my cache anymore. There is plenty information in the Internet though. However, the most important source of information for me was a German forum thread. There I learned 90% about what is needed to develop an application to interface with Teufel/Raumfeld multi-room speakers: HIFI-FORUM - Raumfeld - 3rd-Party Entwickler
I found the Issue.
The constant SPOTIFY_ACTIVE in in hassfeld
constants.py
needs to be lower case. I opened a pull request.
Well done! I just created new releases from hassfeld
and teufel_raumfeld
.
Thanks again for your contribution!
ok, one problem solved, some new ones found:
async_get_media_info
still doesn't work, i kinda know why:
there are two Problems:
If we use spotifyconnect in singleroom mode, we need to work with the Mediarenderer of the speaker, because no one is created for the zone (because no zone exists) for debugging i can overwrite this (2nd problem is far more critical)
if we look at the avtransport.xml
:
<?xml version="1.0"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<actionList>
<action>
<name>SetAVTransportURI</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>CurrentURI</name>
<direction>in</direction>
<relatedStateVariable>AVTransportURI</relatedStateVariable>
</argument>
<argument>
<name>CurrentURIMetaData</name>
<direction>in</direction>
<relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>SetNextAVTransportURI</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>NextURI</name>
<direction>in</direction>
<relatedStateVariable>AVTransportURI</relatedStateVariable>
</argument>
<argument>
<name>NextURIMetaData</name>
<direction>in</direction>
<relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>SetNextStartTriggerTime</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>TimeService</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_WallClockService</relatedStateVariable>
</argument>
<argument>
<name>StartTime</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_WallClockTime</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetPositionInfo</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>TrackDuration</name>
<direction>out</direction>
<relatedStateVariable>CurrentTrackDuration</relatedStateVariable>
</argument>
<argument>
<name>RelTime</name>
<direction>out</direction>
<relatedStateVariable>RelativeTimePosition</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetTransportInfo</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>CurrentTransportState</name>
<direction>out</direction>
<relatedStateVariable>TransportState</relatedStateVariable>
</argument>
<argument>
<name>CurrentTransportStatus</name>
<direction>out</direction>
<relatedStateVariable>TransportStatus</relatedStateVariable>
</argument>
<argument>
<name>CurrentSpeed</name>
<direction>out</direction>
<relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetTransportSettings</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>PlayMode</name>
<direction>out</direction>
<relatedStateVariable>CurrentPlayMode</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Stop</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Rewind</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>Position</name>
<direction>out</direction>
<relatedStateVariable>RelativeTimePosition</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>FastForward</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>Position</name>
<direction>out</direction>
<relatedStateVariable>RelativeTimePosition</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Pause</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Play</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>Speed</name>
<direction>in</direction>
<relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Next</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Previous</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Seek</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>Unit</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_SeekMode</relatedStateVariable>
</argument>
<argument>
<name>Target</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_SeekTarget</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>SetPlayMode</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>NewPlayMode</name>
<direction>in</direction>
<relatedStateVariable>CurrentPlayMode</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>EnterManualStandby</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>EnterAutomaticStandby</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>LeaveStandby</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>GetSpotifyPreset</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>Button</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_ButtonNumber</relatedStateVariable>
</argument>
<argument>
<name>Preset</name>
<direction>out</direction>
<relatedStateVariable>AVTransportURI</relatedStateVariable>
</argument>
<argument>
<name>Metadata</name>
<direction>out</direction>
<relatedStateVariable>A_ARG_TYPE_JsonObject</relatedStateVariable>
</argument>
</argumentList>
</action>
</actionList>
<serviceStateTable>
<stateVariable sendEvents="no">
<name>TransportState</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>STOPPED</allowedValue>
<allowedValue>PLAYING</allowedValue>
<allowedValue>TRANSITIONING</allowedValue>
<allowedValue>NO_MEDIA_PRESENT</allowedValue>
</allowedValueList>
</stateVariable>
<stateVariable sendEvents="no">
<name>TransportStatus</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>AVTransportURI</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>AVTransportURIMetaData</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>CurrentPlayMode</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>NORMAL</allowedValue>
<allowedValue>SHUFFLE</allowedValue>
<allowedValue>REPEAT_ALL</allowedValue>
<allowedValue>REPEAT_ONE</allowedValue>
<allowedValue>RANDOM</allowedValue>
</allowedValueList>
<defaultValue>NORMAL</defaultValue>
</stateVariable>
<stateVariable sendEvents="no">
<name>CurrentTrackDuration</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>PowerState</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>RelativeTimePosition</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="yes">
<name>BufferFilled</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>OwnsAudioResource</name>
<dataType>boolean</dataType>
</stateVariable>
<stateVariable sendEvents="yes">
<name>LastChange</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_InstanceID</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_SeekMode</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>ABS_TIME</allowedValue>
</allowedValueList>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_SeekTarget</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>TransportPlaySpeed</name>
<dataType>string</dataType>
<allowedValueList>
<allowedValue>1</allowedValue>
</allowedValueList>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_WallClockTime</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_WallClockService</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_Boolean</name>
<dataType>boolean</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_ButtonNumber</name>
<dataType>ui4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_JsonObject</name>
<dataType>string</dataType>
</stateVariable>
</serviceStateTable>
</scpd>
there is no 'GetMediaInfo', i think we need to use 'GetSpotifyPreset'
Ok i rewrote async def async_get_media_info(self, zone_room_lst):
for debugging:
async def async_get_media_info(self, zone_room_lst):
"""Get media information of zone."""
room_loc = "http://192.168.178.28:58435/8bb40e1d-8bcb-4636-b062-9f7f7a11e8be.xml"
return await upnp.async_get_spotify_preset(self._aiohttp_session,room_loc)
and upnp.async_get_spotify_preset:
async def async_get_spotify_preset(session,location,instance_id=0,button=1):
"""Return media information."""
action_name = "GetSpotifyPreset"
upnp_action = await get_dlna_action(
location, SERVICE_AV_TRANSPORT, action_name, session=session
)
response = await upnp_action.async_call(InstanceID=instance_id, Button=button)
response["Metadata"] = upnp_action.argument(
"Metadata"
).raw_upnp_value
return response
with this i get {'Preset': 'spotify-preset://?blob=CIGA%2FP%2F%2F%2F%2F%2F%2F%2FwESJHNwb3RpZnk6YWxidW06MU4yMUpIUGw4WVVCZ0lTR2ZZNFNXYxoECAAQASIGCAEQABgAKiRzcG90aWZ5OnRyYWNrOjFpSmZ5NDRIRUs3eFVuNzBHSFFkcVM%3D&pos=0', 'Metadata': '{"active-user":"","spotify-uri":"spotify:album:1N21JHPl8YUBgISGfY4SWc","title":"Die Jungs von AKJ"}'}
as response.
what confuses me: the title
is not the song title but the album title.
I don't know how to get the song title.
If I recall correctly, then preset
is referring to physical buttons on the devices to play a pre-configured media. This could also explain why the metadata is limited to what has to be displayed in the configuration settings for a preset button, e.g. if it is an album the name of the album.
Btw, can you confirm that this bug can be closed? For other aspects not working properly or being missing, don't hesitate to create additional tickets. For general discussion I'd propose to move over to the Discussions area.
As soon as i use my Raumfeld speakers with spotify connect the homeassistant integration stops working. I can't control volume or start/stop songs and also the media card just turns blank: