Make Airplay speakers the voice of HA & other fun MacOS tricks

I don’t think that’s down to curl in particular, it’s iTunes connecting to Airplay speakers. If you sit in front of your Mac and click play (over Airplay speakers) it also takes 2 or 3 seconds for sound to be audible.

1 Like

Thanks for the post. This is a lot easier than the other methods I came across.

I’m running HA as a docker image on macOS. The say command doesn’t work because it’s being executed in the docker image shell and not the osMac shell. I’m being pedantic for those who didn’t know that.

My solution was to use the curl command. I had written a web service for use with my SmartThings hub that invoked the say command or played some short sound files. It runs on the same macOS as my docker HA image and listens on port 8080 so curl 10.0.0.119:8080?speak=this+is+a+test would invoke the say command with the passed text. If you are programmer, writing a web service is pretty trivial. I wrote mine in Rudy. I know it’s also easy in Node.js.

A useful tool for researching is the CLI option in the macOS Docker Desktop. It brings up a shell in the docker image. That’s where I tested the curl command and later found out the ssh worked as well.

Update

Rather than use shell_command for the web service call above, I switched to a rest_command. Here’s an example:

rest_command:
  say_test:
    url: 'http://10.0.0.119:8080?speak=Someone+pressed+the+test+button'

Coming from another Mac-based home automation software I’ve been using Airfoil for ages, however I do not run HA on a Mac, instead I run it on a Pi. This is how I got easy TTS throughout the house using one of my Macs running Airfoil (can be done with Windows as well, but some tweaks would be needed):

I first set up SSH from my Pi to my Mac with a secure key by running this on my HA prompt (replace myname with your username on that Mac and the IP with your Macs IP:

ssh-copy-id [email protected]

With SSH secured I created a shell script on my Mac to run a dynamic TTS. This will be called from the Pi whenever I need to say something to my connected speakers (in my case HomePods). I created a file called ‘tts.sh’ and then `chmod 0755 tts.sh’:

say "$1"

My shell command config looks like this (the rest is as already outlined above for using in automation or whatever):

speak: ssh -i /config/ssh/id_rsa -o 'StrictHostKeyChecking=no' [email protected] '~/Documents/Scripts/tts.sh "{{text}}"'

In all it took maybe 5 minutes to get it all set up. I use similar Applescripts as stated above to start my Airfoil if it’s not running and then auto connect to my various HomePods.

1 Like

As a little update in here, the latest iteration of the AppleTV integration will pair directly with HomePods and other AirPlay 2 devices. I haven’t tried it out yet but it might simplify some of the above

Sounds good. Have you tried playing a sound from a speaker and switch back? How long is the delay?

As above, there is a few-second delay for Airplay 2 speakers to connect, but it’s tolerable. From another post it sounds like something called Shairport-Sync might work but I haven’t mustered the energy to give it a shot and in any case the connection delay is likely the same.

For those of us with multiple Airplay 2 speakers, there doesn’t appear to be a way to select multiple speakers for simultaneous play in HA using the AppleTV integration so I’m still back & forth between applescripting to Airfoil and Better Touch Tool: this is only because Airfoil irregularly supports stereo pairs as Apple keeps changing things and I can’t figure out how to AppleScript Airfoil to connect with a stereo pair.

First world problems.

You can also use the Apple TV integration for this.

It’s broken at the moment and it works with a similar delay. But at least you don’t need any additional software.

But expect it to be buggy. I am not sure if it is Apple side or the integration, but it isn’t reliable.
For me I know play audio alerts directly from the Macs internal speakers. Not as nice, but works and is instantly playing.

This is what I can’t figure out… my model is that all of the output from the server needs to go to multiple Airplay speakers that may or may not be connected. I’ve given up on iTunes and mainly want to use Spotify and the iOS tts services playing to all speakers in sync. Did I miss a way to do that with the Apple TV integration?

In sync is probably not possible. But Maybe you can use Apple Shortcuts to do that and let it trigger by a HA entity in HomeKit.

As a little update here, Better Touch Tool wasn’t working when the screen locked (even with the server set to never sleep) so I went back to the drawing board and figured out how to send keystrokes to Airfoil to select speaker groups, change volume and mute. Very easy once one learns a little bit of AppleScript.
Stereo pairs now working too

#!/bin/bash

osascript <<EOD

  tell application "Airfoil" to activate
  tell application "Airfoil"
  	disconnect from (every speaker)
  end tell
delay 1
  tell application "System Events"
      tell application "System Events" to keystroke "2" using command down
  end tell
delay 2
  tell application "Airfoil"
    set (volume of every speaker) to 0.5
  end tell

EOD
#!/bin/bash

osascript <<EOD

  tell application "Airfoil" to activate
  tell application "System Events"
    key code 125 using {shift down, command down}
  end tell
  
EOD

Yet another update- the above was NOT working with the screen locked, so I’ve come full circle and am back to something similar to my original example which for whatever reason DOES work with the screen locked. It also skips the step of disconnecting from everything before only connecting to the desired outputs.

shell_command:
  play_to_br_hps: "osascript /users/username/.homeassistant/scripts/play_to_br_hps.scpt"
#!/usr/bin/osascript

tell application "Airfoil"
    launch
    activate
	get every speaker
	disconnect from (every speaker whose id contains "Left")
	disconnect from (every speaker whose id contains "Right")
	disconnect from (every speaker whose id contains "Ecob")
	connect to (every speaker whose id contains "Bath")
end tell

-- make sure volume not muted

set vol to output volume of (get volume settings)

if vol < 20 then
	set volume output volume 50
end if

delay 1

While I’m at it here are volume controls directed at the system (so set Airfoil to link volume to the system)- these also work w/ screen locked

#!/usr/bin/osascript

set volume with output muted
#!/usr/bin/osascript

set volume output volume (output volume of (get volume settings) + 5)

And this will make a sensor so you can have your system volume displayed in HA:

sensor:
  - platform: command_line
    name: mini_vol
    command: 'osascript /users/username/.homeassistant/scripts/get_mini_vol.scpt'
    scan_interval: 5
    unit_of_measurement: '%'
#!/usr/bin/osascript

output volume of (get volume settings)
script:
  hps_vol_up:
    sequence:
      - service: shell_command.hps_vol_up
      - service: homeassistant.update_entity
        target:
          entity_id: sensor.mini_vol

Just came across this, this is pretty sweet. How are you targeting different HomePods? Just sshing and running the tts script with the text parameter seems like it would only target everything airfoil is connected to.

This is REAL cool and the solution to my home security system EXCEPT as of Montery the shell command for speaker no longer works… The suggest path "/users/username/.homeassistant/… no long appears valid as Ap[ple continues its user friendly practices

Ok I just can’t seem to find the hidden .homeassisstant. in my home directory or any where else unless I do a link via Samba

Mike- open Script_editor.app and put this code in, then run it. It will list your HomePods for you and you can pick a keyword from each of them:

tell application "Airfoil"
	launch
	activate
	get every speaker
end tell

Then you can use they keyword like I do in the examples above:

	connect to (every speaker whose id contains "Bath")

Stereo pairs appear to be only listed once so Airfoil sees them as a single speaker.

Jason you can hide & unhide hidden files (start with a dot) in Finder using this keyboard combo:
shift-command-period

This is still working for me. Make sure you replace “username” with the actual HA user’s name…

I understand about hidden files and finder.but .homeassistant isn’t there

-r--------    1 jason  staff       7 Nov  1  2021 .CFUserTextEncoding
-rw-r--r--@   1 jason  staff   24580 Jan 13 04:41 .DS_Store
drwx------    3 jason  staff      96 Mar 30 04:35 .Trash
-rw-------    1 jason  staff    9167 Oct 29 14:07 .bash_history
drwx------   33 jason  staff    1056 Oct 26 13:52 .bash_sessions
-rw-r--r--    1 jason  staff      23 Apr 22  2022 .bashrc
drwx------    3 jason  staff      96 Nov  2  2021 .cups
drwxr-xr-x   16 jason  staff     512 Mar 30 05:32 .dropbox
drwxr-xr-x    9 jason  staff     288 Jan 12 13:50 .dynupdater
-rw-------    1 jason  staff    1843 Jan 15 08:41 .history
drwxr-xr-x    4 jason  staff     128 Sep 18  2022 .local
-rw-r--r--    1 jason  staff     257 Apr 22  2022 .profile
drwxr-xr-x    6 jason  staff     192 Sep 18  2022 .pyenv
drwx------    4 jason  staff     128 Jan 12 12:47 .ssh
-rw-r--r--    1 jason  staff    1401 Oct 26 12:27 .tcshrc
drwx------    6 jason  staff     192 Nov  8  2021 .vnc
-rw-r--r--    1 jason  staff     130 Dec  3  2021 .vuescanrc
-rw-r--r--    1 jason  staff      33 Apr 22  2022 .zprofile

I have no problem running the applcript form te Mac I just can’t see how to run from homeassistant