SamDel / ChromeCast-Desktop-Audio-Streamer

Stream the sound of your desktop to your Chromecast Audio device
MIT License
412 stars 30 forks source link

Feature Request: REST interface #61

Closed jerblack closed 4 years ago

jerblack commented 4 years ago

First off, I love this tool. It fixes all the problems I had with Airfoil before, and it's been super reliable. Thank you for building such an awesome tool. The only thing I would like to see added is something list a REST interface so I can integrate this with my Smartthings setup and start controlling this with my voice. I'm looking to be able to say stuff like "Alexa, turn on my bedroom speaker" or "Alexa, set living room speaker to 80%", and while I'm not looking for a full blown skill, it would be nice to have a stable set of URLs I could tell Smartthings to call, which would be enough of a jumping off point to be able to integrate your app into my system. Even Python bindings would be enough to get me going.

SamDel commented 4 years ago

Thanks @jerblack !

I don't know anything about Alexa & Smartthings development. You want the application to open a set of REST interfaces, e.g.:

Is that the only thing you need? Is there documentation about it?

FA-Bubba commented 4 years ago

Just an opinion on this... Given that DAS is primarily a Chromecast tool, which is Google, and because Google and Amazon don't always play nicely with each other, I would rather have a Google interface than Alexa. In fact, we already have that: All of the speakers & Groups are in Google Home, which can control the volume on individual speaker &/or Speaker Groups.

From any Google Speaker, I can already say "Hey Google, increase PlayRoom Speaker Volume to 90%", and the volume is changed immediately.

jerblack commented 4 years ago

Yeah, anything Alexa specific is just an example. All I'm asking for is a generic API or remote control interface into your app that I can hook into with my system. A common way that that's done is with a REST interface providing a structured set of URLs, but if it's simpler just to provide something like a Python API , then I can work with that as well. I don't need anything system specific, just a way to hook into your app.

Your URL examples are basically what I'm talking about:

http://ip:port/all/ <- list all speakers in normalized name format, probably lower-case, punctuation removed, and underscores replacing the spaces, with current connect and volume states http://ip:port/all/50 <- set all speakers to 50% volume http://ip:port/all/off <- disconnect all speakers http://ip:port/all/on <- connect all speakers http://ip:port/bedroom_speaker/on <- connect speaker http://ip:port/bedroom_speaker/off <- disconnect speaker http://ip:port/bedroom_speaker/80 <- set volume to 80% http://ip:port/bedroom_speaker/+20 <- increase volume by 20% from current http://ip:port/living_room_speaker/mute <- mute speaker http://ip:port/living_room_speaker/unmute <- unmute speaker (or just make mute a toggle so subsequent calls toggle status, though deterministic on/off & mute /unmute states is probably easier to work with and more predictable)

Airfoil (the app you replaced in my setup) provided a remote control app that communicated over a socket connection that I was able to use Wireshark to reverse engineer, and then I built a REST API, web interface, and CLI on top of that so I could do pretty much the same thing I'm asking you for here. Here's what I built before if that helps, with an example of the REST urls I put in to control their app. https://github.com/jerblack/remoteFoil

SamDel commented 4 years ago

I like the idea of introducing a generic API that can be used to link in other systems, for custom development, to build a web interface on top of the application, etc.

It should cover all basic functions (as you mentioned): list devices, start, stop, volume, mute. Changing the options of the application, and returning the log can also be added.

Did you also think about the format to use, e.g. jsonapi?

SamDel commented 4 years ago

Setup 2.5.1.zip has a first (quick and dirty) version of the API, can you try?

Supported is:

- http://<ip>:27272/list - list of all devices
- http://<ip>:27272/togglemute - for all devices
- http://<ip>:27272/togglemute/<device name>
- http://<ip>:27272/volume/<device name>/<level> - level between 0 and 100
- http://<ip>:27272/stop - for all devices
- http://<ip>:27272/stop/<device name>
- http://<ip>:27272/start - for all devices
- http://<ip>:27272/start/<device name>
jerblack commented 4 years ago

Yes, I agree that JSON responses would be a good idea and would work for most scenarios. Very cool.

I tested this first build and it's definitely a good start. /list /togglemute and /stop all work. However, anything that requires a device name fails because it can't find the device. My device names look like "Living Room speaker" and "Living Room Chromecast" under /list, so they have spaces and are mixed case, which might be causing a problem.

If I try a url like http://192.168.0.5:27272/stop/Living Room speaker, Chrome automatically escapes the spaces into %20 so it looks like http://192.168.0.5:27272/stop/Living%20Room%20speaker and I get this as the response:

{"errors": { "status": "404 Not Found", "id": "3", "title": "Device not found" } }

If it is a problem with casing and spaces, maybe you could accept a url friendly version of the name with spaces converted to underscores and all lower case. If you do go that route, it would help to include the url friendly version of the name in the JSON response.

As far as the JSON response itself, I don't necessarily need to know the IP and port of each device as long as I know the name. Those IPs and ports are not something I would ever really be interacting with directly. It would be helpful to include the current connect state, volume level, and mute state for each device though. Maybe /all could be simplified to return an array of speakers and groups that look something like this:

[
    {
        "name": "Bedroom speaker",
        "connected": true,
        "mute": false,
        "type": "device",
        "uri_name": "bedroom_speaker",
        "volume": 80
    },
    {
        "name": "Speaker Group",
        "connected": false,
        "mute": false,
        "type": "group",
        "uri_name": "speaker_group",
        "volume": 100
    },
    {
        "name": "Living Room Home",
        "connected": false,
        "mute": false,
        "type": "device",
        "uri_name": "living_room_home",
        "volume": 80
    },
    {
        "name": "Living Room Shield",
        "connected": false,
        "mute": true,
        "type": "device",
        "uri_name": "living_room_shield",
        "volume": 50
    },
    {
        "name": "Living Room speaker",
        "connected": true,
        "mute": false,
        "type": "device",
        "uri_name": "living_room_speaker",
        "volume": 80
    }
]
SamDel commented 4 years ago

In Setup 2.5.2.zip the device names are url-decoded before I use them. That should fix the 'Device not found' issue. I also added volume to /list (and left ip & port in for now).

jerblack commented 4 years ago

That fixed it. /stop /start /volume and /togglemute all work against a speaker now, which is exactly what I needed for my scenario. Thanks!

dday4thedeceiver commented 4 years ago

Fantastic.

This would help with my issue in #55 too if you add a restartrecording endpoint!

@jerblack, out of curiosity, what exactly did you use in your implementation? Personally I would use the Google Home Helper smartapp to add a Virtual Switch and then wire that up to the DAS endpoint with Webcore. Is there a more obvious way I'm missing?

jerblack commented 4 years ago

I am doing something similar. In SmartThings, I used the "Virtual Device Creator" smartapp to create a separate virtual dimmer switch for each speaker, and then I created a WebCore piston that monitored for level and status events from those devices and just mapped those to the appropriate URL. Alexa and Google Home both see these virtual devices as regular dimmer switches, so I can just use commands like turn on/off speaker and set speaker to # to control the volume.

Here's the WebCore piston I am using now to control DAS. https://i.imgur.com/OD72Kbs.png

SamDel commented 4 years ago

In Setup 2.5.3.zip I added /restartrecording:

- http://<ip>:27272/list - list of all devices
- http://<ip>:27272/togglemute - for all devices
- http://<ip>:27272/togglemute/<device name>
- http://<ip>:27272/volume/<device name>/<level> - level between 0 and 100
- http://<ip>:27272/stop - for all devices
- http://<ip>:27272/stop/<device name>
- http://<ip>:27272/start - for all devices
- http://<ip>:27272/start/<device name>
- http://<ip>:27272/restartrecording

Hope it works!

dday4thedeceiver commented 4 years ago

In Setup 2.5.3.zip I added /restartrecording: ... Hope it works!

It works great!

SamDel commented 4 years ago

Thanks for letting me know! ☺️

SamDel commented 4 years ago

It's in release 2.6.