Triggering a remote lamp automatically when I am on a call

I’ve been pondering having some kind of visual-but-not-too-intrusive notification, so that Sandra can see when I am on a call, for some time now. Moving to an office in the garden gave me the final impetus that I needed.

I wanted a simple visual notification that I was on a call, which was triggered automatically. I didn’t want to have to push a button or similar, because I won’t remember.

We settled on it being a lamp in the kitchen: when it is on, I am on a call.

Hardware

The hardware was the easy bit: I had a spare Shelly bulb, already connected to our home IoT VLAN and hooked into Home Assistant.

So all I needed was a way of telling Home Assistant that I am on a call, and toggling the bulb.

Software

I already knew that I wanted to use Home Assistant for controlling the lamp, so the remainder of the puzzle was how to “prod” Home Assistant.

I tend to take calls on either my phone (a Pixel 6 running GrapheneOS) or my laptop (a ThinkPad running Debian).

My phone

The phone was easy, because it has the Home Assistant app on it, and that can already surface various phone states.

In the Home Assistant UI, for the integration with my phone, I had to go into “Entities”, find that - it was “Pixel 6 Phone state” - and enable it, as it was unavailable by default. I clicked on the entity, then the cog icon, and changed the slide to “Enabled”.

I then set up an Automation to toggle the light. I’d expected the trigger to be under “Device”, but it was not - it was under “State”.

And, for some reason, the options for the State of “Phone state” did not match what it showed on screen (only showing “unavailable” and “unknown”, rather than “offthehook”, for example), so I could not trigger based on specific state changes, only that the state has changed.

But that does the trick: “when the phone state changes, toggle the bulb”.

Update: I have also experimented with “Audio mode”. Again, Home Assistant doesn’t seem to have access to the actual values, but just toggling based on change of state seems to suffice, and indeed seems to be better than “Phone state”. So I’ll try that for a while.

My laptop

Had I been using macOS, I think that this might have been quite a lot easier. As I’m not, and I needed to find a way of either detecting when I am on a call, or else proxying it.

Lots of clever and kind people in the fediverse gave me helpful ideas, and the one which I liked the most was to use camera and microphone state as a proxy for on a call. If either or both the microphone or camera was in use, treat it as on a call, and nudge Home Assistant.

Here’s what I came up with:

#!/bin/bash

# Start with the state that I am not on a call

STATE="OFF"

is_mic_on() {

	# Note: for `pactl` to work using systemd, the systemd service needs to be run as the logged in user

	MICOUTPUT=$(/usr/bin/pactl list source-outputs)

	if [[ $MICOUTPUT = "" ]]; then

		echo 0	

	else

		echo 1

	fi

}

is_camera_on() {


	# get lsmod output, and if the first field is "uvcvideo", check the third field

	LSMOD_OUTPUT=$(lsmod | grep uvcvideo)

	readarray LSMOD_ARRAY <<< "$LSMOD_OUTPUT"

	# cycle through the array

	for row in "${LSMOD_ARRAY[@]}";do                                                      
		# check if the first field is "uvcvideo" 

			if [[ $(echo "$row" | awk '{print $1}') == "uvcvideo" ]]; then
	
			# if it is, check if the third field is a "0" (no webcam) or a "1" (webcam on)

				if [[ $(echo "$row" | awk '{print $3}') == "0" ]]; then
								
					echo 0
				
				elif [[ $(echo "$row" | awk '{print $3}') == "1" ]]; then
		
					echo 1
					
				fi


			fi

	done

}


start_actions() {
		
		# Update HomeAssistant switch

		curl -X POST 
  		-H "Authorization: Bearer [authorisation string]" 
  		-H "Content-Type: application/json" 
  		-d '{"state":"on"}' 
  		https://[homeassistant]/api/states/input_boolean.is_neil_on_a_call
}


stop_actions() {

		# Update HomeAssistant switch

		curl -X POST 
  		-H "Authorization: Bearer [authorisation string]" 
  		-H "Content-Type: application/json" 
  		-d '{"state":"off"}' 
  		https://[homeassistant]/api/states/input_boolean.is_neil_on_a_call
}


while :
do


	if [[ "$(is_mic_on)" == 1  || "$(is_camera_on)" == 1 ]]; then

		if [[ "$STATE" == "ON" ]]; then
			

			: # do nothing
		
		else


			start_actions
			STATE="ON"
		fi
	else
		if [[ "$STATE" == "OFF" ]]; then


			: # do nothing

		else
		
			stop_actions
			STATE="OFF"

		fi
	fi


sleep 1

done 

For detecting microphone state, I experimented with both /usr/bin/pactl list source-outputs and find /proc/asound -name status -exec grep -v closed {} +. The problem with the latter was that it also triggered on sound output as well as input, so I went with pactl instead.

For detecting camera state, it is a bit of an ugly processing of command outputs, to find uvcvideo and then get its state.

To poke Home Assistant, I am using its API and a simple cUrl command. I set up a boolean input within Home Assistant, and then connected that to the bulb using an automation. I use this script to change that boolean input.

The script itself is a loop, running every second or so. It uses the STATE variable to avoid constantly sending commands to Home Assistant; instead, it only triggers on change of state.

I am using systemd to start it automatically, with the following in /home/neil/.config/systemd/user/light_on_if_on_a_call.service`:

[Unit]
Description=Toggles a light bulb if I am on a call

[Service]
ExecStart=/home/neil/briefcase/Store/tools/on_a_call/test_for_call.sh

[Install]
WantedBy=default.target

The ExecStart line points to the main script, which is in my user’s home directory.

Note that, to make pactl work, you can’t run it as a normal systemd service, even if you use a User= string. At least, I couldn’t make that work.

Instead, I put it in /home/neil/.config/systemd/user/, and then did systemctl --user daemon-reload, and then enabled it with systemctl --user enable --now light_on_if_on_a_call.service (not using sudo for any of them).

And… it works. Whenever my microphone or my camera, or both, turn on, it toggles the lamp. When they are both off, it turns off the lamp.

There might be neater, or clever, ways of doing this, of course.

If I wanted to take some other kind of action in future, it would be easy to add this to the script.

Things to consider / fix

If I am using the cellular phone app on my phone, it works. If I am using something else - and, these days, if I am calling my wife, I use XMPP - it does not change the state, so I do not get the notification. I wonder if I can fix that, perhaps using Automator or similar on the phone, to poke Home Assistant.

I wonder what impact running this script constantly will have on my battery life. A polling solution is not ideal; a push-based solution would be better, but I don’t know what that might look like.