# Playing Sonos favorite from HomeyScript

**URL:** <https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262>\
**Category:** Developers\
**Created:** [May 10, 2021, 5:07pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262 "2021-05-10T17:07:19Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 10, 2021, 5:07pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/1 "2021-05-10T17:07:19Z")

</div>

Not sure if this question is for developers forum but it definitely something that you guys shoud be able to help me. I am looking in to using HomeyScript to do some more elaborate flows. In order make it work I am planning to use Homey.flow.runFlowCardAction. One of the action cards I want to run is “cloud\_play\_sonos\_favorite”. I am trying to do it using following code:

```
await Homey.flow.runFlowCardAction({
  uri: 'homey:device:<speaker id>,
  id: 'cloud_play_sonos_favorite',
  args: [{ 
      name: '<favorite name>', 
      type: 'autocomplete', 
      title: 'Favorite' 
  }]
});

```

I am getting following error message:

⚠ Cannot read property ‘id’ of undefined: Cannot read property ‘id’ of undefined  
at /node\_modules/athom-api/dist/index.js:1:1186403  
at processTicksAndRejections (internal/process/task\_queues.js:97:5)

What am I missing here. I see this card has autocomplete argument so maybe I need to provide favorite value differently. Or this action card can not be called from script? Can we use all action card available in flows or there are limitations?

---

<div class="post-metadata">

**Author:** ![Edwin\_D](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/edwin_d/32/136417_2.png) [@Edwin\_D](https://community.homey.app/u/Edwin_D)\
**Post date:** [May 10, 2021, 6:08pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/2 "2021-05-10T18:08:39Z")

</div>

Theres a ‘ missing in the uri line. The string is not closed. I have no experience with Sonos, so I cannot be of much help with how to use the card. Values enclosed in \<\> should usually be replaced with actual values though.

---

<div class="post-metadata">

**Author:** ![Jero](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/jero/32/17556_2.png) [@Jero](https://community.homey.app/u/Jero)\
**Post date:** [May 10, 2021, 6:24pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/3 "2021-05-10T18:24:55Z")

</div>

```
// get the card for the device
// you need to find the device id first
const uri = 'homey:device:32d7a90e-abcb-41f1-ba09-04f833e96565';
const id = 'action_project_task';
const card = await Homey.flow.getFlowCardAction({ uri: uri, id: id });

// you dont really need this you can hardcode arg.name in getFlowCardAutocomplete
// this is just to show the relation between them
const arg = card.args.find((arg => arg.name === 'project'));

// the result of this is a list with all the possible options for this argument
// so select the one you want and pass it to runFlowCardAction
// you can also hardcode this in runFlowCardAction
const options = await Homey.flow.getFlowCardAutocomplete({
  uri: uri,
  id: id,
  name: arg.name,
  query: '',
  type: `flowcardaction`
})

Homey.flow.runFlowCardAction({
  uri: uri,
  id: id,
  // key value pairs
  // args[0] is an object in this case
  args: {
    [arg.name]: options[0],
    // hardcoded arg example
    content: 'Test'
  }
})

```

Example here for a different card but the principle is the same. You can find the device id in the developer tools or use HomeyScript to find it.

---

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 10, 2021, 6:35pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/4 "2021-05-10T18:35:37Z")

</div>

Hey thanks Edwin. Yes those values shod be replaced with real ones. And yes closing ’ is missing but that is just here not in my test code :).

---

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 10, 2021, 6:36pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/5 "2021-05-10T18:36:49Z")

</div>

Jero that is what I need, how to handle autocomplete parameters in general. Thanks 🙂 will try it.

---

<div class="post-metadata">

**Author:** ![Edwin\_D](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/edwin_d/32/136417_2.png) [@Edwin\_D](https://community.homey.app/u/Edwin_D)\
**Post date:** [May 10, 2021, 6:55pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/6 "2021-05-10T18:55:42Z")

</div>

Hmm. I don’t think it is funny that I spend time to find an error in your code that is not the code that gave the error. Sorry would fit better than smile.

---

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 10, 2021, 7:14pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/7 "2021-05-10T19:14:57Z")

</div>

Edwin I didn’t want to be disrespectful. I definitely apricate your help, and I also know that people don’t understand how important is to enclose your string values or what value between \<\> represent. So I know you were trying to help. I am developer my self so I sometimes forget that things like that are not obvious to everybody.

Sorry if offended you it was definitely not my intention.

---

<div class="post-metadata">

**Author:** ![Edwin\_D](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/edwin_d/32/136417_2.png) [@Edwin\_D](https://community.homey.app/u/Edwin_D)\
**Post date:** [May 10, 2021, 7:19pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/8 "2021-05-10T19:19:35Z")

</div>

Apology accepted and incident forgotten. From the initial question it is impossible to tell the experience level of the writer. So sorry I stated the obvious too.

---

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 10, 2021, 7:37pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/9 "2021-05-10T19:37:42Z")

</div>

I only need name of the argument and id of the value (when argument is from the list of options). So If somebody else stumbles on this topic it looks something like this:

```
wait Homey.flow.runFlowCardAction({
  uri: 'homey:device:<device id>',
  id: 'cloud_play_sonos_favorite',
  args: { 
     favorite: { id: "<option id>"}  
  }
});

```

Where “favorite” is name of the argument in this case and id is id of the favorite in options.

---

<div class="post-metadata">

**Author:** ![Marcel\_Ubels](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/marcel_ubels/32/18860_2.png) [@Marcel\_Ubels](https://community.homey.app/u/Marcel_Ubels)\
**Post date:** [May 11, 2021, 6:06pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/10 "2021-05-11T18:06:46Z")

</div>

Have you thougt of using the api from Sonos?  
[Sonos Api](https://github.com/jishi/node-sonos-http-api). Here a link with a how to [Sonos api how to](https://community.homey.app/t/sonos-say/6697)

You can install it on a server(with Docker) and or a Rpi. With get requests you are able to control Sonos. It works great. This is a flow i use to play a radiostation or a playlist from Sonos favorites

> **[Radiozender opzoeken | Homey](https://homey.app/en-us/flow/nUkCsB/)**

This are some possibilities with the Sonos api

## Favorites

It now has support for starting favorites. Simply invoke:

`http://localhost:5005/living room/favorite/[favorite name]`

and it will replace the queue with that favorite. Bear in mind that favorites may share name, which might give unpredictable behavior at the moment.

## Playlist

Playing a Sonos playlist is now supported. Invoke the following:

`http://localhost:5005/living room/playlist/[playlist name]`

and it will replace the queue with the playlist and starts playing.

With a script i shuffle my radiostations and playlists every 2 weeks

This way you also have the ability to easily raise or lower the volume

 ![image](https://us1.discourse-cdn.com/flex025/uploads/athom/original/3X/d/7/d70c15a60bbb07e1b00b834808484c1b128e5e6f.jpeg)

---

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 11, 2021, 6:35pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/11 "2021-05-11T18:35:47Z")

</div>

Thanks for letting me know. However I can do all of that in script as well without need for the server. So for now I am ok with using script that runs on homey.

---

<div class="post-metadata">

**Author:** ![Marcel\_Ubels](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/marcel_ubels/32/18860_2.png) [@Marcel\_Ubels](https://community.homey.app/u/Marcel_Ubels)\
**Post date:** [May 11, 2021, 6:49pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/12 "2021-05-11T18:49:13Z")

</div>

Can you share your script?

---

<div class="post-metadata">

**Author:** ![igorstefus](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/igorstefus/32/34815_2.png) [@igorstefus](https://community.homey.app/u/igorstefus)\
**Post date:** [May 11, 2021, 8:03pm UTC](https://community.homey.app/t/playing-sonos-favorite-from-homeyscript/47262/13 "2021-05-11T20:03:05Z")

</div>

Sure. This is the script I written today to control speakers using fibaro keyfob. In it you have how to group, ungroup speakers, play favorites, increase and decrease volume and also how to mute and unmute.  
I still didn’t do full testing of it but it should give you idea how to use homey script to control speakers. It combines usage of `runFlowCardAction` and `setCapabilityValue`. to achieve what I need. It am not using playlists but you just need to use playlist action card same way I am using play favorite action card. (fibaro keyfob part is removed as it is not necessary here).

```
//Devices.
const bedroomSpeaker = await Homey.devices.getDevice({id:`<here you put speaker device id>`});
const livingRoomSpeakers = await Homey.devices.getDevice({id:`<here you put speaker device id>`});
const kitchenSpeaker = await Homey.devices.getDevice({id:`<here you put speaker device id>`,});
const romeSpeaker = await Homey.devices.getDevice({id:`<here you put speaker device id>`});

//Volume.
const normalVolume = await Homey.logic.getVariable({id:`<varibale id>`});
const roamNormalVolume = await Homey.logic.getVariable({id:`<varibale id>`});
const volumeIncrement = await Homey.logic.getVariable({id:`<varibale id>`});

//Actions.
const actionPlayFavorit = `cloud_play_sonos_favorite`;
const actionJoinSpeaker = `cloud_join_player`;
const actionLeaveCurrentGroup = `cloud_leave_current_group`;

//Capabilities
const capabilitySetVolume = `volume_set`;
const capabilityMute = `volume_mute`;

//Sonos Favorites.
const favorites = await Homey.flow.getFlowCardAutocomplete({
  uri: `homey:device:${livingRoomSpeakers.id}`,
  id: actionPlayFavorit,
  name: `favorite`,
  query: ``,
  type: `flowcardaction`
});

const dasko_i_mladja = favorites[0];
const flow = favorites[1];
const indie_gold = favorites[2];
const jazz_london_radio = favorites[3];
const kexp_fm = favorites[4]; 
const punk_fm = favorites[5]; 

//Helper functions.

async function joinAllSpeakers()
{
  await runFlowCard(bedroomSpeaker, actionJoinSpeaker, livingRoomSpeakers);
  await runFlowCard(kitchenSpeaker, actionJoinSpeaker, livingRoomSpeakers);
  await runFlowCard(romeSpeaker, actionJoinSpeaker, livingRoomSpeakers);
}

async function ungroupAllSpeakers()
{
  await runFlowCard(bedroomSpeaker, actionLeaveCurrentGroup);
  await runFlowCard(kitchenSpeaker, actionLeaveCurrentGroup);
  await runFlowCard(romeSpeaker, actionLeaveCurrentGroup);
}

async function setNormalVolumeToAllSpeakers()
{
  await setSpeakerVolume(bedroomSpeaker, normalVolume.value);
  await setSpeakerVolume(kitchenSpeaker, normalVolume.value);
  await setSpeakerVolume(romeSpeaker, roamNormalVolume.value);
}

async function playFavoriteOnAllSpekaers(favorite)
{
  await setNormalVolumeToAllSpeakers();
  await runFlowCard(livingRoomSpeakers, actionPlayFavorit, favorite)
  await joinAllSpeakers();
}

async function joinSpeaker(device)
{
  await runFlowCard(device, actionJoinSpeaker, livingRoomSpeakers);
}

async function ungroupSpeaker(device)
{
  await runFlowCard(device, actionLeaveCurrentGroup);
}

async function decreaseSpeakerVolume(device)
{
  const volume = getSpeakerVolume(device) - volumeIncrement.value; 
  await setSpeakerVolume(device, volume);
}

async function decreaseAllSpeakersVolume()
{
  await decreaseSpeakerVolume(livingRoomSpeakers);
  await decreaseSpeakerVolume(bedroomSpeaker);
  await decreaseSpeakerVolume(romeSpeaker);
  await decreaseSpeakerVolume(kitchenSpeaker);
}

async function pauseAllSpeakers()
{
  await muteSpeakerVolume(livingRoomSpeakers, true)
  await muteSpeakerVolume(kitchenSpeaker, true)
  await muteSpeakerVolume(bedroomSpeaker, true)
  await muteSpeakerVolume(romeSpeaker, true)
}

async function increaseSpeakerVolume(device)
{
  const volume = getSpeakerVolume(device) + volumeIncrement.value; 
  await setSpeakerVolume(device, volume);
}

async function increaseAllSpeakersVolume()
{
  await increaseSpeakerVolume(livingRoomSpeakers);
  await increaseSpeakerVolume(bedroomSpeaker);
  await increaseSpeakerVolume(romeSpeaker);
  await increaseSpeakerVolume(kitchenSpeaker);
}

async function playAllSpeakers()
{
  await muteSpeakerVolume(livingRoomSpeakers, false)
  await muteSpeakerVolume(kitchenSpeaker, false)
  await muteSpeakerVolume(bedroomSpeaker, false)
  await muteSpeakerVolume(romeSpeaker, false)
}

function getSpeakerVolume(device)
{
  return device.capabilitiesObj.volume_set.value;
}

async function muteSpeakerVolume(device, mute)
{
  await Homey.devices.setCapabilityValue({ deviceId:device.id, capabilityId: capabilityMute, value: mute} )
}

async function setSpeakerVolume(device, value)
{
  await Homey.devices.setCapabilityValue({ deviceId:device.id, capabilityId: capabilitySetVolume, value: value} )
}

async function runFlowCard(device, actionId, argument = null)
{
  var argName = argument? await getFlowCardArgName(device, actionId) : null;

  return await Homey.flow.runFlowCardAction({
    uri: `homey:device:${device.id}`,
    id: actionId,
    args: argument? { [argName]: argument } : []
  });
}

async function getFlowCardArgName(device, actionId)
{
  const card =await Homey.flow.getFlowCardAction({
    uri: `homey:device:${device.id}`,
    id: actionId
  });

  return card.args[0].name;
}
```
