Liver64/LoxBerry-Sonos

Request: Add audioClip support (for possible doorbell chime)

Opened this issue · 11 comments

Letrab commented

audioClip is supported locally too: bencevans/node-sonos#530 .

As opposed to the cloud API : https://devdocs.sonos.com/reference/audioclip-loadaudioclip-playerid

This would also solve my old problem: #25
(For which I am using my own Node.JS based application for: https://github.com/Letrab/hassio-addons/tree/master/sonos-audioclip-tts . But it would be nice to be able to drop that, and use the functionality of Loxberry-Sonos here... )

What do you think? Would you have time to implement it? Or are you open to pull requests?

Thanks in advance!

I still aware of this function, but due to the fact the Plugin is not based on the Sonos API I didn‘t spend any time to implement the audioclip.
One reason is that each user needs his API key in order to use Sonos API.

Generally I’m open for pull requests

Letrab commented

As bencevans/node-sonos#530 (comment) suggests, it seems the API key isn't checked on local access. So we can use something static for that.

Will see if I can come up with a PR/proposal!
What's your general development strategy? Local file changing on your Loxberry setup? Or can you clone/double install a plugin somehow?

Thanks in advance!

sounds interest if it works…
I use actually a 2nd dev Pi and since 2 weeks I set-up a Hyper-V VM, but not using it right now for dev.
Hyper-V is free of charge for Windows.

Letrab commented

I have basic audioClip via local HTTPS API working (proof of concept)

Proper integration is needed (local/remote URLS, multiple/all players (synchronised?)).

  • cleanup (what to do with the GUID, just take a static one?), php documentation

But seems promising (I almost can ditch my custom NodeJS script/server)

function guidv4($data = null) {
    // Generate 16 bytes (128 bits) of random data or use the data passed into the function.
    $data = $data ?? random_bytes(16);
    assert(strlen($data) == 16);

    // Set version to 0100
    $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
    // Set bits 6-7 to 10
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80);

    // Output the 36 character UUID.
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

/**
* Funktion : audioclip_post_request --> POST to https url of player
*
* @param: 	$ip, $rincon
* @return: JSON
**/	
  
function audioclip_post_request($ip, $rincon) {
	
	global $volume;

	// API Url
	$url = 'https://'.$ip.':1443/api/v1/players/'.$rincon.'/audioClip';
	
	// Initiate cURL.
	$ch = curl_init($url);
	 
	// populate JSON data.
	$jsonData = array(
		'name' => "randomName",
		'appId' => 'de.loxberry.sonos',
		'clipType' => 'CHIME',
		'volume' => $volume
	);
		 
	// Encode the array into JSON.
	$jsonDataEncoded = json_encode($jsonData);
		 
	// Tell cURL that we want to send a POST request.
	curl_setopt($ch, CURLOPT_POST, 1);
	 
	// Attach our encoded JSON string to the POST fields.
	curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonDataEncoded);
	 
	// Set the content type to application/json
	$headers = [
		'Content-Type: application/json',
		'X-Sonos-Api-Key: '.guidv4(),
	];
	curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 

	// Accept peer SSL (HTTPS) certificate
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
	
	// Request response from Call
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		 
	// Execute the request
	$result = curl_exec($ch);
	
	// was the request successful?
	if($result === false)  {
		LOGGING("Play_T2S.php: audioclip_post_request error: ".curl_error($ch), 7);
	} else {
		LOGGING("Play_T2S.php: audioclip_post_request okay!", 7);
	}
	// close cURL
	curl_close($ch);
	return $result;
}


/**
* New Function for gong: audioclip
*
* @param: empty
* @return: nothing
**/

function playAudioclip() {
	global $sonoszone;
			
	if(isset($_GET['zone'])) {
		LOGDEB("ZONE ".$sonoszone[$_GET['zone']][0]);
		$player = $sonoszone[$_GET['zone']];
		audioclip_post_request($player[0], $player[1], 20);
	} else {
		// sendgroupmessage();
	}	
}
Letrab commented

Branched from the php8Fixes branch, but:

WIP branch : https://github.com/Letrab/LoxBerry-Sonos/tree/audioClip

Forgot to mention that in the old documentation was stated not all Sonos players are supporting this command, but the new documentation does not mention anything. So should be checked:
https://devdocs.sonos.com/reference/audioclip-loadaudioclip-playerid

Letrab commented

Old documentation:
https://web.archive.org/web/20230330171701/https://developer.sonos.com/reference/control-api/audioclip/

This namespace is experimental. Some of the functionality may not yet work as documented. Sonos supports audio clips on new players and players that have a microphone. This includes Sonos One, Amp, Port, Play:5 (gen 2), Beam, One SL, Move, as well as the SYMFONISK table lamp with WiFi and SYMFONISK WiFi bookshelf speakers.

But maybe they ported it to all devices? I don't have a device not listed above to test on...

Thanks for your work, looks great. I will have a look and try to optimize it. Actually I’m in process finalization of a new Plugin. Once finished I spend some time on audioclips. From the documentation I’ve read that TTS as audioclip is also possible…

Letrab commented

Thanks!
Probably will add/change some things as well.

Indeed, a URL also could be loaded to play. But it should be via a HTTP(s) server. So either a remote file on the web, or a local file on LAN http server. So the local mp3 files should be served via the loxberry http frontend. Is this the case already?

To check whether a player supports this API, we could perform once (when scanning the player? But then you need to re-scan when you want to use the new audioClip API), this call:

curl -v --insecure \
  -H 'X-Sonos-Api-Key:123e4567-e89b-12d3-a456-426655440000' \
  https://192.168.1.40:1443/api/v1/players/local/info

It responds with containing this JSON entry:
"capabilities":["CLOUD","PLAYBACK","AIRPLAY","VOICE","AUDIO_CLIP"] where we can check whether AUDIO_CLIP is supported...

we can add the local MP3 file once received from TTS creation and push then to audioclip.

Plugin sends to TTS Provider and after creation the answer been captured and saved as MP3

can you send a PR please.