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

There’s a good discussion of the install types here.

I used the core method, which involves creating a VENV and is the most technically challenging of the installs, but you’ll learn a hell of a lot doing it. I don’t think the supervised install will run on MacOS and the HA OS method replaces the operating system entirely so you’d only do that on like an RPi (unless it can be containerized as it sounds like maybe you did with a VM). The last choice requires one to figure out Docker (which I didn’t) so I followed the technique linked in the first sentence of this entire topic.

So the discussion et forth ohttps://www.home-assistant.io/installation/macos. Is in fact two different approaches not one. That is where I went wrong. Like I sad what you are attempting with the TTS is what I really want to have. Sometime my current approach Eem like here are three different layer at the same time, Linux , HA OS and Mac OS. I think you re right about the container. I will save my current systems as I provide everything I want so far except for TTS and five the Core Approach a shot… Thanks will let you know ho it goes so others may profit from my experience I take Add On ad Intergrations still work with e Core Approach ?

ok ae ir a shot. but I depend. on Zwave. Apparently that becomes an "advanced topic"and past the installation guid and most likely beyond this noobie.

@fmon - I know this topic is years old, but it’s the one that finally got me (almost) fully sorted for whole home audio and video (in sync!) and I am tremendously grateful for your write ups.

Like - seriously - thank you!

This might be pushing my luck but I wanted to ask:

Have you ever done anything with home assistant and airfoil to control volume with a slider?

I have two Airport Express endpoints that I use with this, and they don’t expose volume controls - so I have to use airplay to set the volume of those two sets of speakers in the house. Everything else I just leave airfoil at 100% and control the volume directly on the devices.

This isn’t too hard and mostly I just set them statically when I make any of our home assistant one button switches (music or video really is the big switch we do, but also have buttons to turn/off on any of 7 speaker zones) but then I have to manually use Airfoil if I want to make an adjustment from the defaults I set. Just for those two speaker sets. All the others are “true” airplay 2 targets and I can control volume from a typical media player type slider.

I’m presuming I could so something with applescript like:

#!/bin/bash
osascript <<EOD
  tell application "Airfoil"
    get (volume of every speaker whose name starts with "speakerName")
  end tell
EOD

Get that volume and store it some helper
Set the initial value of some slider to whatever it comes back with
and then I guess?
write a script that on change of that slider would do a tell airfoil action to set volume.

That seems right, but I’m failing to come up with a way to get it to actually work and wondered if you had done anything like that already that you could share?

If not - of course no worries at all, and for real thanks a ton. Yours was the write up that got me to solve something I was banging my head against the wall on for months and months. And it’s glorious. This is just an attempt to make it a little bit better than it already is.

Haha I love it… and glad to help!

You raise an interesting point… a slider is more elegant and would take up less space than my comparably clunky volume buttons.

The first problem is that sometimes (and I still haven’t been able to figure out how to predict this) AppleScripting to Airfoil doesn’t always work if the screen is locked- this is why I’m still linking to the system volume and controlling everything at once (which always works). In your instance you may have to caffeinate and set the screen to never lock- but try it and see.

The sensor you have right. This works on my system:

tell application "Airfoil"
	get (volume of every speaker whose id contains "Living")
end tell

So now we have to pass a variable (in this case the new input_number after a change of the slider) to an outside osascript. The following is how I did it for overall system volume- you’ll have to modify this for your specific Airfoil outputs.

  set_mini_vol: 'osascript /users/user/.homeassistant/scripts/set_mini_vol.scpt {{ states("input_number.mini_vol_slider") }}'

and the script:

#!/usr/bin/osascript

on run argv
	set volume output volume (item 1 of argv)
end run

automate the slider:

- id: 'id_38'
  alias: "change system volume wth slider"
  trigger:
    - platform: state
      entity_id: "input_number.mini_vol_slider"
  condition: []
  action:
    - service: shell_command.set_mini_vol
    - service: homeassistant.update_entity
      target:
        entity_id: sensor.mini_vol

Well I don’t know what to say, this looks like something I can work with!

I’m going to play around with this tomorrow and see if I can’t get something going.

Thanks so much, I’ll follow up when I have a chance to give this a go!

Today I managed to make a sensor to query Airfoil and update an input_select based on speaker groups- set up whatever unique combinations you want in Airfoil. Since you can select multiple speaker groups at once in Airfoil and that would confuse things, I first disconnect from all speakers each time I request a change. To get the internal ID of each unique speaker combination, connect to it on Airfoil and run this in Script Editor; I tested and these IDs do survive a server restart. Note the entry for each speaker group that begins with “com.rogueamoeba.group”

tell application "Airfoil"
	get (every speaker that connected is true)
end tell

Now make a command_line sensor that runs that same code (and one for mute if you want). I decided to automate an input_select off of the sensor:

- id: 'id_39'
  alias: "set connected speaker group"
  trigger:
    - platform: state
      entity_id: sensor.connected_speakers
    - platform: state
      entity_id: sensor.speakers_mute_sensor
  condition: []
  action:          
    - service: input_select.select_option
      target:
        entity_id: input_select.speakers
      data:
        option: >
          {% if states("sensor.speakers_mute_sensor") == "true" %}muted
          {% elif "long-string" in states("sensor.connected_speakers") %}bed/bath
          {% elif "long-string" in states("sensor.connected_speakers") %}liv rm
          {% elif "long-string" in states("sensor.connected_speakers") %}indoor
          {% elif "long-string" in states("sensor.connected_speakers") %}outdoor
          {% elif "7long-string" in states("sensor.connected_speakers") %}all
          {% else %}---
          {% endif %}

Then update your external scripts to call those same groups. You can reverse this process with a dropdown or do like this and put the input_select in a custom_button_card that pops up a speaker selection panel:

Following on that last with an improved way to do this by sensing each speaker that is connected rather than groups. Part of the problem I had before was that the Airfoil output is quite lengthy if you have a few speakers, and sensor states in HA have a limit of 255 characters. I couldn’t figure out how to move it into an attribute, but I figured out how to shorten up the state to just the given name of the speakers:

  - platform: command_line
    name: connected_speakers
    command: 'osascript /users/user/.homeassistant/scripts/get_connected_speakers.scpt'
    value_template: >
      {% for word in value.split() if "@" in word %}
      {{ word[12:] }}
      {%- endfor %}

This literally yields a state like
@Near @Far @Bedroom @Bath
Also, now that HA knows each individual speaker to which it’s connected rather than just the speaker group, it’s no longer necessary to disconnect everything first and then reconnect, so everything happens much faster

Hi everybody, to be honest I’m a beginner with coding and I’m really struggling to install the apple code. Does someone has A to Z video tutorial ?
Thank you so much for the help !

Hi Tom,

I’m going to assume that you know how to create a basic bash script sort of thing and that you’re asking specifically about AppleScript. I don’t know about tutorials but you can learn a lot by playing around with ScriptEditor.app which you’ll find in Applications → Utilities on your mac. To see what’s available in a specific app, click Window → Library (if it’s not already open) and add the app. Then create a blank script and start adding stuff to the top window, then click play to see if it works. Then you can save the script somewhere like config/scripts and call them as shell_command.script_name.

1 Like