NixOS Coffee
Yeah but will NixOS make my coffee in the morning?
NixOS 21.11 was released a few days ago, so in the process of spamming nixos links in the usual places someone responded with a question for the ages:
So I responded with some pseudocode that should accomplish that:
services.coffee = {
enable = true;
time = "14:00";
};
To my surprise, nixos doesn't actually have a module for this, though. I have an ubiquiti mfi networked power strip with a fairly simple API so I decide to make one. This is simple enough that it can be lifted straight from the nixos manual's locate service example.
First the boilerplate:
{ config, pkgs, lib, ... }:
let
cfg = config.services.coffee;
in {
}
Define the interface:
options.services.coffee = {
enable = lib.mkEnableOption "brew coffee";
time = lib.mkOption {
type = lib.types.str;
default = "09:00";
description = ''
Turn on kettle at 09:00 via api call to power strip
'';
};
sessionID = lib.mkOption {
type = lib.types.str;
default = "01234567890123456789012345678901";
description = ''
Session ID for the api request
'';
};
ipAddress = lib.mkOption {
type = lib.types.str;
default = "10.200.1.5";
description = ''
IP address of the mfi device
'';
};
apicall = lib.mkOption {
type = lib.types.str;
default = "curl -X POST -d \"username=ubnt&password=ubnt\" -b \"AIROS_SESSIONID=${cfg.sessionID}\" http://${cfg.ipAddress}/login.cgi && curl -X PUT -d output=1 -b \"AIROS_SESSIONID=${cfg.sessionID}\" http://${cfg.ipAddress}/sensors/1";
};
};
Add the systemd service and timer:
config = {
systemd.services.brew-coffee = {
description = "Turn on power strip";
path = [ pkgs.curl ];
script = ''
${cfg.apicall}
'';
};
systemd.timers.brew-coffee = {
description = "Update time";
partOf = [ "brew-coffee.service" ];
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = cfg.time;
};
};
All of that wrapped up in a nice christmas package:
{ config, pkgs, lib, ... }:
let
cfg = config.services.coffee;
in {
options.services.coffee = {
enable = lib.mkEnableOption "brew coffee";
time = lib.mkOption {
type = lib.types.str;
default = "09:00";
description = ''
Turn on kettle at 09:00 via api call to power strip
'';
};
sessionID = lib.mkOption {
type = lib.types.str;
default = "01234567890123456789012345678901";
description = ''
Session ID for the api request
'';
};
ipAddress = lib.mkOption {
type = lib.types.str;
default = "10.200.1.5";
description = ''
IP address of the mfi device
'';
};
apicall = lib.mkOption {
type = lib.types.str;
default = "curl -X POST -d \"username=ubntmin&password=ubnt\" -b \"AIROS_SESSIONID=${cfg.sessionID}\" http://${cfg.ipAddress}/login.cgi && curl -X PUT -d output=1 -b \"AIROS_SESSIONID=${cfg.sessionID}\" http://${cfg.ipAddress}/sensors/1";
};
};
config = {
systemd.services.brew-coffee = {
description = "Heats water for coffee";
path = [ pkgs.curl ];
script = ''
${cfg.apicall}
'';
};
systemd.timers.brew-coffee = {
description = "Coffee timer";
partOf = [ "brew-coffee.service" ];
wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = cfg.time;
};
};
}
Add the module to my config:
imports = [
./coffee.nix
];
services.coffee = {
enable = true;
time = "15:00";
};
Time to rebuild the system and see what needs to be fixed, but
To my surprise it actually worked first try (forgetting to include lib
up top notwithstanding). Sweet! A few minutes later I hear a relay click and check the power strip to confirm the good news.
And the services:
That was surprisingly headache free.
A small confession: It actually just started the kettle, but that's good enough for government work.