Tags:
It is quite useful to hook automations to the start or shutdown of certain computers. For instance, you can set to turn on or off certain lights
or use the start up of a server as a condition to run other automations that
depend on it. Here I will explain how I use nix systemd services together with
sops-nix
to send events from my computers managed through nix to my
home-assistant server.
Home-assistant API
First, we need to check the home assistant API specification.
To send events
, we need to send a POST
to the endpoint: /api/events/<event_name>
.
To do so, we need to activate the api
integration by adding api:
into our
configuration.yaml
. And then, we can obtain the API token in
http://$HASS_ADDR:8123/profile
(where $HASS_ADDR
is the address of your
home-assistant instance). From this point on, $HASS_TOKEN
refers to this home-assistant API token.
A useful tool to check events is http://$HASS_ADDR:8123/developer-tools/event
,
which allows us to subscribe to any event and watch the data that we receive.
We can use this to try subscribing to a testing
event and sending the
following curl
request to check that everything is working:
curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hello" : "world"}' \
$HASS_SERVER/api/events/testing
We can also use hass-cli which abstracts the API calls through a handy cli:
hass-cli event fire testing --json '{"hello" : "world"}'
If everything went well, curl
should return {"message":"Event testing fired."}
and in home-assistant
the following entry will be present:
event_type: testing
data:
hello: world
origin: REMOTE
time_fired: "2023-07-22T12:13:49.674053+00:00"
context:
id: **************************
parent_id: null
user_id: *******************************
Systemd services
For my use case I wanted to send 4 different events:
- Screen locked / unlock
- System startup / shutdown
Screen lock/unlock
Since I use i3lock
for my lock screen, I wrapped the call around event fires
as follows:
#!/usr/bin/env bash
JSON_DATA="{\"hostname\":\"$(hostname)\"}"
curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_DATA" \
$HASS_SERVER/api/events/nixos.lock
i3lock "$@" --nofork
curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_DATA" \
$HASS_SERVER/api/events/nixos.unlock
Notice the --nofork
flag, without it, the lock will be non-blocking and the
script will continue executing without waiting for the screen to unlock.
With nix
and home-manager, you can wrap i3lock
like this:
{config, pkgs, ...}:
{
home.packages = [
(pkgs.writeShellScriptBin "i3lock" ''
${pkgs.curl}/bin/curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hostname" : "${config.networking.hostName}"}' \
$HASS_SERVER/api/events/nixos.lock &
${pkgs.i3lock-color}/bin/i3lock-color --nofork "$@"
${pkgs.curl}/bin/curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hostname" : "${config.networking.hostName}"}' \
$HASS_SERVER/api/events/nixos.unlock
'')
];
}
System startup / shutdown
Now, we need to specify the systemd
service that sends home-assistant
the startup and shutdown events. This will be a Type=oneshot
since
it will only run the script once, and we want RemainAfterExit=true
so that ExecStop
runs on system shutdown. Since for the request
to work we need network connection, we have to set network-online.target
as both wants and after:
[Unit]
Description=Notify Home Assistant of startup/shutdown
Wants=network-online.target
After=network-online.target
[Install]
WantedBy=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/hass-startup
ExecStop=/usr/local/bin/hass-shutdown
RemainAfterExit=true
The scripts in /usr/local/bin/hass-{startup,shutdown}
contain the curl calls
for startup and shutdown messages. Note that you have either hardcode the
HASS_TOKEN
in the scripts or on the service Environment
parameters.
Another option is to use EnvironmentFile
pointing to a file in a secure
location with the key.
In NixOS
In NixOS
, we can define the systemd
service as shown:
{config, pkgs, ...}:
{
systemd.services = {
hass-online = {
enable = true;
unitConfig.Description = "Notify Home Assistant of boot completion";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
script = ''
${pkgs.curl}/bin/curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hostname" : "${config.networking.hostName}"}' \
$HASS_SERVER/api/events/nixos.online
'';
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# sops-nix secrets:
EnvironmentFile = config.sops.secrets.hass_env.path;
SupplementaryGroups = [ config.users.groups.keys.name ];
ExecStop = pkgs.writeShellScript "hass-offline" ''
UPTIME="$(cut -d' ' -f1 /proc/uptime)"
IDLETIME="$(cut -d' ' -f2 /proc/uptime)"
NPROC="$(nproc --all)"
${pkgs.curl}/bin/curl -X POST \
-H "Authorization: Bearer $HASS_TOKEN" \
-H "Content-Type: application/json" \
-d "{ \"hostname\": \"${config.networking.hostName}\", \"cores\": $NPROC, \"uptime\": $UPTIME, \"idletime\": $IDLETIME }" \
$HASS_SERVER/api/events/nixos.shutdown
'';
};
};
};
}
The script can be modified to send other information and even take parameters
from the NixOS config
module (such as config.networking.hostname
for the hostname).
Here I also send the uptime and idle time of the computer when it shuts down, so I
can track my computer usage trends with home-assistant.
Sops-nix
In the above snippet, I have included EnvironmentFile = config.sops.secrets.hass_env.path;
This is the path of a file managed by sops-nix which contains the $HASS_TOKEN
and $HASS_SERVER
variables.
The hass.yaml
file has this format when unencrypted:
hass_env: |
HASS_TOKEN=<************************************>
HASS_SERVER=http://10.0.0.123:8123
Which means that it can be used as EnvironmentFile
in systemd
services or
in bash
by sourcing it.
Sample home-assistant automation
A simple example of the use of the lock screen events is to turn on or of the lamp on my desk when I leave the desk (lock the computer):
alias: Screenlock controls desk_lamp
description: "Turn off desk lamp on lock and back on when I log back"
trigger:
- platform: event
event_type: nixos.lock
event_data:
hostname: kuro
condition:
- condition: state
entity_id: light.desk_lamp
state: "on"
action:
- service: light.turn_off
data: {}
target:
entity_id: light.desk_lamp
- wait_for_trigger:
- platform: event
event_type: nixos.unlock
event_data:
hostname: kuro
continue_on_timeout: false
- service: light.turn_on
data: {}
target:
entity_id: light.desk_lamp
mode: single