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.
You may also like:
- PINE64's PineBuds Pro: my first impressions
- Disabling my ThinkPad's internal camera on boot
- Snikket, a self-contained XMPP distribution
- GSConnect (KDE Connect), WireGuard, and Debian 12 Bookworm
- Unlocking a LUKS-encrypted partition via ssh on Debian 12 Bookworm
- NetworkManager: automatically switch between Ethernet and Wi-Fi
- Fixing sogo's 'incorrect string value' error
- Updating the LUKS key derivation function on Debian
- Backing up to a USB stick automatically via udev
- Fixing espanso incomplete text replacement
- Automating actions in Nautilus (GNOME's file manager) with scripts
- Jabra Evolve2 40 and Debian Linux
- Debian on a £190 Lenovo ThinkPad X1 Yoga Gen 2
- Enabling Webauthn in Firefox via snap
- Decrapifying (mostly) an Amazon Fire 8 HD Kids tablet via Linux