With the original sonos binding you can already play announcements, however depending on how you have previously started the music, music will not continue after the announcement.
With this solution I am using sonos cloud service to publish a sound file via the audioClip function.
Tested with OH3.4.1
Disadvantage: The sonos cloud service is not registered as audiosink, therefore you can only use mp3/wav files for notification but not a built in TTS service.
This solution consists out of three parts:
A. One-time installation: Creation & setup of sonos developer account
B. using a rule to manage api access within OH
C. using a script to send the actual notification (can be triggered by any other script or rule)
Step A: Creation & setup of sonos developer account
- Go to https://integration.sonos.com/ and create a new developer account or sign-in with your existing account
- Create a new control integration & credentials / a key for your integration (you can use https://www.google.com as redirect url and do not need to specify an event callback url)
- Open the following URL in your browser, login with your regular sonos account and authorize the new integration:
https://api.sonos.com/login/v3/oauth?client_id=<Your_Client_Credential_Key>&response_type=code&state=<Your_Test_State>&scope=playback-control-all&redirect_uri=<redirect url>
- Once you are logged in and forwarded to the redirect URL, you will see the authorization code in the URL.
- Create an access & refresh token via the sonos dev page: createToken
5.1. Use your api key & secret from step 2 for basic-authentication (at the right side of the page)
5.2 add your authorization code from step 4 as code (in the middle of the page)
5.3 Click the “Try It” button to execute the request and you will get an access token & refresh token. Store both for later usage.
Step B: Manage API Access within OH
- Create a new string item, that will store your access token (access token is only valid for 24hours and needs to be renewed daily)
- Install JSONPATH transformation if you have not done it before
- Create a new rule, running every 24 hours (e.g. always at 3am in the morning) to refresh the access token and use the following DSL script:
var access_token_item = "<enter item name from step 1>";
var refresh_token = "<enter refresh token from part A, step 5.3>";
var auth = "<enter basic authentication from part A, step 5.1>";
var headers = newHashMap("authorization" -> "Basic "+auth, "accept"-> "application/json");
var result = sendHttpPostRequest("https://api.sonos.com/login/v3/oauth/access?grant_type=refresh_token&refresh_token="+refresh_token, "application/json", '',headers,10000);
var new_access_token = transform("JSONPATH", "$.access_token", result);
postUpdate(access_token_item, new_access_token);
- Please note: In case the HTTP request will fail (e.g. no network connection, cloud issue, etc) your access_token_item will contain invalid data. You can always manually run the rule to get a new access token at any time
Step C: Sending an actual notification
- Get the unique device name from your sonos thing
- Create a new script, add the following javascript code and adjust the placholders:
var x, headers, token, sonos_speaker, url, body, http, accessTokenItem;
http=Java.type("org.openhab.core.model.script.actions.HTTP");
sonos_speaker = '<add the unique device name from step 1>';
accessTokenItem = '<add the name of your item from part B, step 1>';
// Sends an MP3 file to Sonos device as notification
function notify_sonos(x) {
token = itemRegistry.getItem(accessTokenItem).getState();
url = 'https://api.ws.sonos.com/control/api/v1/players/'+sonos_speaker+'/audioClip';
body = ['{"name":"Notification","appId":"","priority":"HIGH","clipType":"CUSTOM","streamUrl":"'+x+'"}';
headers = [];
headers["Authorization"] = "Bearer "+token;
headers["WWW-Authenticate"] = "Basic";
var returnvalue = http.sendHttpPostRequest(url, "application/json", body,headers, 10*1000);
}
if (String(ctx['URL']) != 'undefined') {
notify_sonos(ctx['URL']);
} else if (String(ctx['item']) != 'undefined') {
notify_sonos(['<URL OF YOUR OH SYSTEM>/static/sounds/',ctx['item'],'.mp3'].join(''));
}
Note 1: I have stored mp3 files in the /html/sounds folder, what will make these mp3 files available via /static/sounds per web. In addition I have named the mp3 files same as the item, whats triggering the notification (e.g. if a frontdoor_doorbell_switch item will trigger a doorbell sound the mp3 file has the same name), so that I can either pas a complete URL as parameter to the script or simple the name of an item and the script will automatically build the correct URL.
Note 2: The sound file needs to be reachable by the sonos speaker
Note 3: due to personal laziness as well as copy & past of other rules I have created the rule to refresh the token in DSL, but the notification script itself in javascript. Feel free to adopt.
Step D: Sample Usage
In order to execute the script from step C, simple add the following javascript code to your rule (there is also a blockly block to execute another rule / script and this code is generated by blockly):
var ruleManager = addFrameworkService('org.openhab.core.automation.RuleManager');
function convertDictionaryToHashMap (dict) {
if (!dict || dict.length === 0) return null;
var map = new java.util.HashMap();
Object.keys(dict).forEach(function (key) {
map.put(key, dict[key]);
});
return map;
}
ruleManager.runNow('<ID of your script>', true, convertDictionaryToHashMap({'URL': 'http://<OH local IP>:8080/static/sounds/sample_announcement.mp3'}));
Note: Should also work with https, but I have not tested if sonos will validate & accept any custom certificate.