xchwarze / samsung-tv-ws-api

Samsung Smart TV WS API wrapper
GNU Lesser General Public License v3.0
268 stars 44 forks source link

[TODO] Volume feature support #101

Open xchwarze opened 2 years ago

xchwarze commented 2 years ago

The feature seems to be on all TVs (Legacy, Encrypted and Tizen) via UPnP, but in the information api (https://192.168.1.xxx:8002/api/v2/) I can't see any indicator if it is present/enabled. Maybe there to implement it and if the request fails throw an error that it is not supported.

xchwarze commented 2 years ago

Code examples https://github.com/xchwarze/ha-samsungtv-custom/pull/3/commits/9e27100fae8105d0556e55c809d1bf89f84a9e36 https://github.com/jaruba/ha-samsungtv-tizen/blob/master/custom_components/samsungtv_tizen/upnp.py

epenet commented 2 years ago

IMO, we should make upnp async.

Maybe we could use this as a dependency? https://github.com/StevenLooman/async_upnp_client

xchwarze commented 2 years ago

Yes, and the implementation would be similar to what they did here https://github.com/balmli/com.samsung.smart/blob/master/lib/UPnPClient.js

epenet commented 2 years ago

This is what I just tried at my end, using pip install async_upnp_client.

import aiohttp
import asyncio
import logging

from async_upnp_client.aiohttp import AiohttpSessionRequester
from async_upnp_client.client_factory import UpnpFactory
from async_upnp_client.exceptions import UpnpActionResponseError

logging.basicConfig(level=logging.DEBUG)

host = "1.2.3.4"
SVC_RENDERINGCONTROL = "urn:schemas-upnp-org:service:RenderingControl:1"

async def main():
    async with aiohttp.ClientSession() as session:
        upnp_requester = AiohttpSessionRequester(session)
        upnp_factory = UpnpFactory(upnp_requester)
        upnp_device = await upnp_factory.async_create_device(f"http://{host}:9197/dmr")

        svc_renderingcontrol = upnp_device.service(SVC_RENDERINGCONTROL)

        get_volume_response = await svc_renderingcontrol.action("GetVolume").async_call(InstanceID=0, Channel="Master")
        print(f"GetVolume: {get_volume_response}")

        get_mute_response = await svc_renderingcontrol.action("GetMute").async_call(InstanceID=0, Channel="Master")
        print(f"GetMute: {get_mute_response}")

        try:
            set_mute_response = await svc_renderingcontrol.action("SetMute").async_call(InstanceID=0, Channel="Master", DesiredMute=True)
            print(f"SetMute: {set_mute_response}")
        except UpnpActionResponseError as err:
            print(f"Failed to SetMute: {err.__repr__()}")

        try:
            set_volume_response = await svc_renderingcontrol.action("SetVolume").async_call(InstanceID=0, Channel="Master", DesiredVolume=50)
            print(f"SetVolume: {set_volume_response}")
        except UpnpActionResponseError as err:
            print(f"Failed to SetVolume: {err.__repr__()}")

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Sadly it seems to always return bad values on my TV:

epenet commented 2 years ago

I have updated my sample above.

These are my results:

GetVolume: {'CurrentVolume': 0}
GetMute: {'CurrentMute': False}
Failed to SetMute: UpnpActionResponseError('Error during async_call(), status: 500, upnp error: 501 (Action Failed)')
Failed to SetVolume: UpnpActionResponseError('Error during async_call(), status: 500, upnp error: 501 (Action Failed)')