Spotifynd – Spotify music that automatically follows you as you move around your home!

This project started off as an implementation of a new use case that my Comprehensive Room Presence Detection project now allows. As usual, it ended up down many rabbit holes and with screens full of debug nodes in Node-RED. But the end result is pretty fun! Spotifynd can be let loose to transfer any music you start playing on Spotify (on your phone or desktop clients etc.) to speakers in the room you are in. Then, if you move to a different room, your music will follow you and switch to playing on speakers in the new room. This is all thanks to the great Spotcast integration. All this can happen automatically with no user intervention. Or, Spotifynd can be set to notify you before starting if you’re not quite ready to let it run wild. Actionable notifications give you more detailed control whenever Spotifynd is running or encounters a problem.

Download – You can download a copy of all the flows below:

Title picture describing this project which combines room presence detection and Spotify playback to transfer and move your music to wherever you go in your home.
This project combines room presence detection and Spotify playback to transfer and move your music to wherever you go in your home. States are stored in Home Assistant while the logic is processed in Node-RED.

DISCLAIMER: This is a private project with no affiliation to Spotify.

What Spotifynd CAN (and does) do –

  • Automatically transfer whatever music you start to play on Spotify (on any device) to any cast devices in your room. (Note – Spotcast is advertised as working on chromecast devices and I don’t have other media players to test this on).
  • Automatically transfer music to a cast device in another room you move to while Spotifynd is ‘following’ you.
  • Ask you whether you would like Spotifynd to take control of your music (via actionable Android notifications) if you’d rather Spotifynd didn’t start working automatically.
  • Spotifynd won’t start following you around with music if you have headphones in and music won’t be transferred to media players that are already playing something else. In both cases, an override actionable notification is sent.
  • Notifications are also sent for various scenarios where Spotifynd was unable to start.

What Spotfynd CANNOT do –

  • Work instantly – expect some time (up to ~20s) before music playback is detected. This seems to be a limitation of the Spotify Home Assistant integration. During this delay, music will still be playing on the original device.
  • Work instantly when you move between rooms. Again, expect a delay while room presence is established (~20 sec usually when multiple people are present and tracked, less otherwise). Once Spotifynd has started there is no additional delay when moving music between rooms other than being detected.
  • Fancy fading – there’s no implementation of any fading in a previous room before fading up in the new room (yet!). Media players will play at whatever volume they were previously at.
  • Work perfectly when Spotifynd is following multiple people. Currently, Spotifynd is set up not to overwrite any existing media already playing on devices. So, in the current implementation, the music for the latest person to enter the room would be stopped while the original person’s music would continue. The latest person to enter the room will be prompted with an option to override the existing music. In this case, the original person in the room would have their music stopped.

Requirements:

  • Home Assistant – All the various states and controls are stored and retrieved from Home Assistant.
  • Some form of room presence detection (see my previous guide for what I use). (You could modify Spotifynd to be a sort of automated version of the Lovelace spotify-card but extra steps are added here so that the media player is set based on which room a person is in instead).
  • The Home Assistant Spotify integration – this is used to trigger the flows once Spotify playback starts on a device. Each person needs their own media player entity that keeps track of their current playback status. For this integration, it’s helpful if they are all of the form media_player.spotify_p1 where p1 is the initials of Person 1 etc.
  • Node-RED – I hadn’t learned any code before starting out in Home Assistant, so all my automations are configured in Node-RED (now with increasing use of the function node). A couple of nodes in the flows also use the wider ability to access the states of other Home Assistant entities (made possible by node-red-contrib-home-assistant-websocket). Install that too if you don’t already have it.
  • Spotcast – Spotcast is really the star of the show. This does the job of sending the playback to the relevant media player once what and where to play it has been established. Again, each user will need their own Spotcast account for this to work. The force_playback feature of Spotcast allows you to start up playback on idle devices which cannot be achieved otherwise.

In Home Assistant

I use two input_booleans and an input_select for each person to keep track of whether Spotifynd should run and the current state of Spotifynd for them. These can be set up with the helpers function of Home Assistant or manually in configuration.

For an example person (Person 1 or p1):

  • input_boolean.spotifynd_p1 – stores whether Spotifynd should run at all (either triggered automatically or after a notification prompt is clicked).
  • input_boolean.spotifynd_auto_p1 – stores whether or not Spotifynd should run automatically (without first asking the user).
  • input_select.spotifynd_p1 – this stores the current state of Spotifynd. This input select is important to keep track of the many modes that Spotifynd could be in and to handle more advanced user interaction (asking to transfer music but then not follow or transfer to a previous room etc.).

Example config for input_boolean.spotifynd_p1 and input_boolean.spotifynd_auto_p1:

input_boolean:
  spotifynd_p1:
    name: Spotifynd P1
    icon: mdi:account-music
  spotifynd_auto_p1:
    name: Auto Spotifynd P1
    icon: mdi:account-check

Example config for input_select.spotifynd_p1:

input_select:
  spotifynd_p1:
    name: Spotifynd P1
    initial: idle
    icon: mdi:account-music
    options:
      - suggesting
      - declined
      - starting
      - following
      - playing_not_following
      - idle
      - issue
      - stop
#These options will be detailed later
Click to hide example configurations

In Node-RED

As I mentioned, Home Assistant keeps track of the states and ties things together, but all the logic here runs in Node-RED. Through these flows, I’ve tried to minimise the number of parameters that need to be hard-coded. If you follow similar naming conventions to the ones I use, you will only actually need to modify 4 areas in this entire project to get it working for you. Wherever hard-coding is required, I’ve added a comment with a * and an explanation of why and what needs to be changed to work for you too. In general, I’ve utilised naming conventions and regex notation in nodes to dramatically reduce the number of nodes needed overall. In most flows, a msg.person is set near the start and subsequent nodes will only apply to that person. Granted, these simplifications only work if you too follow similar naming conventions. Otherwise it would be a case of modifying start of flows to set the msg.person according to your naming conventions. This is quite achievable with some Node-RED knowledge.

1. Save your headphone’s MAC address

This first node saves info about each person’s headphones.

NodeRED Code 1: Showing how to save your headphones' details manually on startup.
Node-RED 1: Saving your headphones’ details manually on startup

If you start music on your phone while your headphones are connected, it’s likely you don’t actually want to listen to music out loud. So, in this case, Spotifynd won’t automatically start under any circumstances. Instead, the relevant person is notified and is given the option to override and start Spotifynd anyway.

There’s some hard-coding required here since these details are unique for everyone. In total, three properties are saved in flow variables for each person:

  1. Save the entity which stores the connected Bluetooth devices. When using the Android app, this is something like sensor.*your_device*_bluetooth_connection.
  2. Save the MAC address for the headphones of the person. If you don’t know this, just try watching the states of your sensor while you connect your headphones. The sensor updates quickly in my experience and you’ll see the new entry under the connected devices of the sensor which corresponds to your headphone’s MAC address.
  3. Save the name that Spotify displays when music is playing via your phone. You can get this name by checking the source property of the relevant Spotify account of your Spotify integration when playing music from your phone.

I trigger this node each time Node-RED starts up (check here for some ideas on that) so the flow variables are always set when I need them. The Android app also reports if wired headphones are plugged in so you could modify this to check for that instead. Alternatively, if you don’t care about Spotifynd starting when you have headphones in you can ignore/delete this entirely.

2. Keep input_select.spotifynd_*person* updated

Each person’s input_select.spotifynd_*person* entity keeps track of their overall Spotifynd status. It’s basically a variable that is adjusted whenever Spotifynd is started, declined or is set not to run at all amongst other options. Other flows can also then trigger based on changes to this input_select. This flow is triggered by their Spotify playback going to anything except playing or by changes in a person’s input_boolean.spotifynd_*person*.

NodeRED Code 2: An imaged describing a flow to keep input_select.spotifynd_*person* updated. Each route that sets the input_select is wired in to a final node at the bottom where the state is actually updated.
Node-RED 2: A flow to keep input_select.spotifynd_*person* updated. Each route that sets the input_select is wired into a final node at the bottom where the state is actually updated.

The node with the person icon is where the msg.person is set for that flow. Wherever you see this icon, msg.person is being set for that particular flow. Since msg.person is set right at the start (based on exactly who’s entity triggered the flow), we can have everyone’s info run along the same nodes without interference. This is utilised at the start of many flows so we don’t have to repeat them all for each new person who could have Spotifynd activated.

This first part of this section can set your input_select to either idle or stop. Also included here is something that turns a person’s input_boolean.spotifynd_auto_*person* to off if their input_boolean.spotifynd_*person* is turned off.

The lower half sets your input_select to declined if you decline to start Spotifynd via notifications sent by other sections. Setting your input_select to declined also triggers a 10-minute delay which will change the selection from declined to idle (this timer is cancelled if your input_select goes to anything other than declined). This logic basically triggers a 10-minute cooldown before Spotifynd can be started again after anyone declines it. All the nodes with arrow icons don’t actually do anything, they’re just added neaten the wiring and reduce the spaghetti mess that usually happens.

All flows that can set anyone’s input_select terminate at a single node via link nodes so it’s easy to see what can cause changes.

3. Initialise Spotifynd when Spotify playback is started

This next bit is triggered whenever you or someone else starts Spotify playback on any device.

NodeRED code 3: This image describes a NodeRED flow triggered by anyone starting Spotify playback on any device. This flow either starts Spotifynd automatically (if allowed) or ask the user for permission. If playback is started on a phone with headphones connected, a separate notification is sent.
Node-RED 3: A flow triggered by anyone starting Spotify playback on any device. This flow either starts Spotifynd automatically (if allowed) or ask the user for permission. If playback is started on a phone with headphones connected, a separate notification is sent.

Since the event: state node uses Regex notation, we can have everyone’s Spotify integration entity trigger the flow. It also means that we can avoid a bunch of hard coding in person-specific entity names. After msg.person is set, there follows a series of checks before continuing. The first check is that the person’s input_select is idle. The state of this input_select is updated in a separate flow but the idle state is set when Spotifynd has not transferred the person’s media anywhere and is not following the person around the home with music either.

Next, there’s a check that the person is home (we don’t need to bother anyone playing music away from home) and third there’s a check to see if the person’s headphones are connected. This uses the flow variables mentioned previously. If the source of the person’s Spotify playback is their phone and they have their headphones connected, then the message will continue via the bottom path. Otherwise, it will follow the top path. If there’s no issue with headphones, then there’s a final check if Spotifynd should automatically start. Each of the three cases (auto start, ask before start and headphones issue) are wired to separate notifications that are sent in a further flow.

4. Sending notifications

Where possible, I try to avoid sending notifications since in my view it defeats a large part of what many consider to be true home automation (ie minimising user input and interaction). But, for a couple of cases, I’ve found it helpful to send a notification to allow various features that would otherwise not be possible.

Any notifications that are sent by Spotifynd are all grouped here with the actionable notifications available from them being actioned just below.

NodeRED Code 4: This image shows the nodes that make up 'The notification centre'. All notifications are configured here in the top set of flows. They are actually sent at the seperate group of nodes below. Finally, any presses of actionable notifications (such as the option to stop Spotifynd) are all handled by the group at the bottom.
Node-RED 4: ‘The notification centre’. All notifications are configured here in the top set of flows. They are actually sent at the seperate group of nodes below. Finally, any presses of actionable notifications (such as the option to stop Spotifynd) are all handled by the group at the bottom.

The following (actionable) notifications are sent in the circumstances listed:

  1. When Spotifynd is started automatically:
    • Before the notification is sent, set input_select.spotifynd_*person* to starting.
    • Notification options:
      1. Ok – does nothing but gets rid of the notification.
      2. Ok + Auto off – turns input_boolean.spotifynd_auto_*person* off so that Spotifynd isn’t automatically started next time.
      3. Stop – stops playback, sets the person’s input_select to declined and turns off input_boolean.spotifynd_auto_*person*.
  2. When Spotifynd is ready to start (but isn’t allowed to start automatically):
    • Before the notification is sent, set the person’s input_select to suggesting.
    • Notification options:
      1. Yes – sets the person’s input_select to starting.
      2. Yest + Auto on – sets the person’s input_select to starting and turns on input_boolean.spotifynd_auto_*person*.
      3. No – does nothing but gets rid of the notification.
  3. When Spotifynd isn’t started because playback is started on a phone with headphones connected:
    • Notification options:
      1. Override – start Spotifynd playback anyway.
  4. When Spotifynd isn’t started because media is already playing in the room that music would be transferred to:
    • Notification options:
      1. Override – start Spotifynd playback anyway.
  5. When Spotifynd can’t start because the media player in the room music should start in is unavailable
    • Send a notification alerting the relevant person.
  6. When Spotifynd can’t start because the person is in a room/location with no media players assigned.
    • Send a notification alerting the relevant person.
  7. When Spotifynd has transferred music and is following you (I’ve set this to be a persistent + sticky notification so you can accidentally get rid of it):
    • Notification options:
      1. Stop following – Don’t stop media but don’t transfer room to room. Sets the person’s input_select to playing_not_following.
      2. Stop music – stops playback, sets the person’s input_select to declined and turns off input_boolean.spotifynd_auto_*person*.
      3. Prev. Room – Move media to the previous room the person was in but don’t transfer room-to-room afterwards (ie stop following). Sets the person’s input_select to playing_not_following.

Note – the notification sent while music is following is not resent when the person moves to a new room and it is automatically dismissed whenever someone’s input_select goes to anything other than following.

5. Use Spotcast to start/transfer music

Starting playback and transferring music to a new media player (or group) can be triggered in several ways. These are all grouped and handle in the final set of nodes.

NodeRED Code 5: Image showing the group of nodes that handle starting Spotifynd to intialise or transfer music between rooms using Spotcast. Various check also alert the user if Spotifynd cannot start for some reason.
Node-RED 5: Starting Spotifynd to intialise or transfer music between rooms using Spotcast. Various check also alert the user if Spotifynd cannot start for some reason.

Ways that Spotifynd is actually started:

  1. Manually via a script: If each person has a script.spotifynd_manual_start_*person* then triggering that will start Spotifynd for the relevant person. I added a further feature that also sets a specific room based on the script executed. For example, executing a script.spotifynd_manual_start_bedroom_*person* will start playing the relevant person’s music in the Bedroom. Modification on this can be a neat addition if you have NFC tags or buttons. Now they can start whatever Spotify music you were listening to in your room. (Follow mode is still activated with these scripts).
  2. Via a Notification: Override – if the override actionable notification is pressed when Spotifynd doesn’t start because headphones are connected or if media is already playing on a device, this will trigger Spotifynd to start anyway
  3. Via a Notification: Previous Room – While Spotifynd is following you, one of the actionable notification options is to send music to the previous room you were in. Pressing this does what it says on the tin and will also stop Spotifynd from following you from that point.
  4. input_select changing to starting – If Spotifynd is allowed to start via the notification that is send when input_boolean.spotifynd_auto_*person* is off, the person’s input select is set to starting and Spotifynd is triggered from this change
  5. A person location change. Each person’s room is saved to an input_select.*person*_location. Whenever that changes, the relevant msg.person is set and Spotifynd is started if the person’s input_select is following

For each of these triggers that force Spotifynd to start (ways 1, 2 and 3), the relevant media player where music should be started is set manually. I prefer to do this manually here since speaker group names can vary or you may not wish to start media on all media players in a room. This happens in the Set Room/Set Previous Room function nodes. When setting a previous room, the person’s input_select.*person*_location is searched and the last but one entry is set.

Next, the Set data function node sets the data needed for Spotcast to start. This varies a little depending on if it’s the primary user or someone else with a Spotcast account name who is triggering Spotcast, so some hard coding is required here. The current room where media is set to be played is also saved here. This allows the correct music to be turned off in each case if requested via an actionable notification. After this Spotcast is triggered and music is transferred to the relevant media player. Finally, if Spotifynd is starting up in the first room (and this isn’t after already moving room while Spotifynd was following them), the person’s input_select is changed to following and the relevant notification is sent. The reason this node only triggers in the first room so you don’t get a new notification each time you move rooms.

When Spotifynd isn’t forced to start (ways 4 and 5), there’s a check that the media player (or group) in the person’s room is not playing anything. If not, the flow follows a similar path to the manual triggers above. However, if the media player that is due to be used is not off, there is a check that it is not unavailable (if it were unplugged etc.). If it is, then a notification is sent alerting the user. Otherwise, the flow continues.

If the media player is not off or unavailable, there’s a check to make sure that it isn’t your music that is playing. This can happen if you start Spotify playback and cast it straight to a media player. In this case, there is no need for Spotcast to do anything right now and the input_select should just go to following and send a notification.

If something is playing that isn’t your Spotify music, then a different notification will be sent allowing you to instead override whatever is playing with your Spotify music.

After this, the person’s input_select is changed to following and they are sent the corresponding notification that gives them the option to stop Spotifynd following them, stop playback altogether or transfer music to the previous room and stop following them.

Conclusion

That’s all there is to Spotifynd – it’s really just a tool that combines room presence detection and Spotcast. It works alongside the Spotify app so your usual interactions are not interrupted. Instead, Spotifynd adds features that make for a nicer user experience. After a short time, the music following you as you move between rooms becomes really natural and a nice addition. The extra notification controls are really there for times when Spotifynd isn’t required, like if you’re moving between many rooms in quick succession without really stopping anywhere.

All the extras like checking if media is already playing in your room and headphones checks are designed to be easily removed should you not need them. Hopefully, the reduced hardcoding required will also make it easier for you to integrate this into your homes if this is something that interests you.

Epilogue

I just wanted to add this short piece to serve as a cautionary tale in reading all the documentation of things you are using (spoiler: I didn’t). When I started, I gave Spotcast a quick go and managed to start playing a specific playlist really quickly. From there, I didn’t read further and see that Spotcast also offers the option to simply transfer whatever is already playing to a specific media player. Without realising how easy this is with Spotcast, I started trying to figure out how to do it myself. It turns out that the Home Assistant Spotify integration tells you the uri of the track you’re playing but this is no good if that track is part of a playlist (since only the track would be played once). I ended up using the node-red-contrib-spotify node and in a roundabout way, I could get the playlist/album uri and then get an array of all the tracks in the playlist. Then I would search this array and match the playing track to figure out the position in the playlist. I also had to get and save how far into each song the user was and redo this each time music was being transferred between rooms. I’m happy to say that it worked and I learned a lot about searching arrays along the way but had I actually read a few lines further down, I would have seen that I actually need to configure less (I now don’t even need to specify what to play at any point) to achieve what I wanted quicker. It’s a testament to the great features of Spotcast that it’s so simple to achieve what is otherwise far more complicated.

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top