manatee: Add bluetooth reset handling
This commit is contained in:
@@ -6,6 +6,60 @@
|
||||
}:
|
||||
let
|
||||
nginxEnabled = config.mod.nginx.enable;
|
||||
|
||||
script = pkgs.writeShellScript "bt-reset" ''
|
||||
set -euo pipefail
|
||||
export PATH="${
|
||||
lib.makeBinPath [
|
||||
pkgs.bluez
|
||||
pkgs.util-linux
|
||||
pkgs.kmod
|
||||
pkgs.gnugrep
|
||||
pkgs.coreutils
|
||||
]
|
||||
}"
|
||||
|
||||
logger -t bt-reset "Starting Bluetooth adapter reset..."
|
||||
|
||||
# Exit early if the adapter is already present and running
|
||||
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
|
||||
logger -t bt-reset "hci0 is already UP RUNNING — nothing to do"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If hci0 exists but isn't UP, try bringing it up
|
||||
if hciconfig hci0 2>/dev/null; then
|
||||
logger -t bt-reset "hci0 exists but not running — bringing it up"
|
||||
hciconfig hci0 up || true
|
||||
sleep 2
|
||||
|
||||
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
|
||||
logger -t bt-reset "hci0 is UP now"
|
||||
systemctl restart bluetooth.service
|
||||
logger -t bt-reset "bluetooth.service restarted — done"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Hard reset: reload the btusb kernel module (works for USB adapters)
|
||||
logger -t bt-reset "hci0 missing — reloading btusb module..."
|
||||
modprobe -r btusb 2>/dev/null || true
|
||||
sleep 3
|
||||
modprobe btusb
|
||||
sleep 3
|
||||
|
||||
if hciconfig hci0 2>/dev/null; then
|
||||
hciconfig hci0 up
|
||||
logger -t bt-reset "hci0 restored after module reload"
|
||||
else
|
||||
logger -t bt-reset "ERROR: hci0 not found after module reload"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Restart the bluetooth systemd service so bluetoothd picks up the adapter
|
||||
systemctl restart bluetooth.service
|
||||
logger -t bt-reset "bluetooth.service restarted — done"
|
||||
'';
|
||||
in
|
||||
{
|
||||
hardware.bluetooth.enable = true;
|
||||
@@ -57,71 +111,111 @@ in
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Trigger reset via udev when hci0 disappears
|
||||
udev.extraRules = ''
|
||||
ACTION=="remove", SUBSYSTEM=="bluetooth", KERNEL=="hci0", \
|
||||
TAG+="systemd", ENV{SYSTEMD_WANTS}+="bt-reset.service"
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.user = {
|
||||
timers = {
|
||||
"update-hetzner-ha-dns" = {
|
||||
unitConfig = {
|
||||
Description = "updates Hetzner DNS for home-assistant";
|
||||
};
|
||||
systemd = {
|
||||
services = {
|
||||
# Trigger reset on bluetoothd failure
|
||||
bluetooth = {
|
||||
unitConfig.OnFailure = [ "bt-reset.service" ];
|
||||
};
|
||||
|
||||
timerConfig = {
|
||||
Unit = "update-hetzner-ha-dns.service";
|
||||
OnCalendar = "*-*-* *:00/30:00";
|
||||
Persistent = true;
|
||||
};
|
||||
bt-reset = {
|
||||
description = "Reset Bluetooth adapter";
|
||||
after = [ "bluetooth.service" ];
|
||||
|
||||
wantedBy = [ "timers.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = script;
|
||||
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10s";
|
||||
StartLimitIntervalSec = "120";
|
||||
StartLimitBurst = 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services = {
|
||||
"update-hetzner-ha-dns" = {
|
||||
unitConfig = {
|
||||
Description = "updates Hetzner DNS for home-assistant";
|
||||
timers.bt-reset = {
|
||||
description = "Periodically reset Bluetooth adapter";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnBootSec = "5min"; # first run 5 min after boot
|
||||
OnUnitActiveSec = "4h"; # then every 4 hours
|
||||
RandomizedDelaySec = "5min";
|
||||
};
|
||||
};
|
||||
|
||||
user = {
|
||||
timers = {
|
||||
"update-hetzner-ha-dns" = {
|
||||
unitConfig = {
|
||||
Description = "updates Hetzner DNS for home-assistant";
|
||||
};
|
||||
|
||||
timerConfig = {
|
||||
Unit = "update-hetzner-ha-dns.service";
|
||||
OnCalendar = "*-*-* *:00/30:00";
|
||||
Persistent = true;
|
||||
};
|
||||
|
||||
wantedBy = [ "timers.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "exec";
|
||||
EnvironmentFile = config.age.secrets.hetzner-dns.path;
|
||||
services = {
|
||||
"update-hetzner-ha-dns" = {
|
||||
unitConfig = {
|
||||
Description = "updates Hetzner DNS for home-assistant";
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "exec";
|
||||
EnvironmentFile = config.age.secrets.hetzner-dns.path;
|
||||
};
|
||||
|
||||
path = [
|
||||
pkgs.curl
|
||||
pkgs.coreutils # For `cat`
|
||||
pkgs.jq
|
||||
];
|
||||
|
||||
script = ''
|
||||
LAST_IP_FILE="/tmp/hetzner-dns-ha-ip"
|
||||
INTERFACE="enp3s0"
|
||||
|
||||
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
|
||||
|
||||
LAST_IP=""
|
||||
if [[ -f "$LAST_IP_FILE" ]]; then
|
||||
LAST_IP=$(cat "$LAST_IP_FILE")
|
||||
fi
|
||||
|
||||
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
|
||||
echo "IP unchanged, NOOP update."
|
||||
exit 0
|
||||
else
|
||||
echo "Updating IP"
|
||||
|
||||
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
|
||||
|
||||
curl \
|
||||
--fail \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_BODY" \
|
||||
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/ha/A/actions/set_records" \
|
||||
&& echo $CURRENT_IP > $LAST_IP_FILE
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
path = [
|
||||
pkgs.curl
|
||||
pkgs.coreutils # For `cat`
|
||||
pkgs.jq
|
||||
];
|
||||
|
||||
script = ''
|
||||
LAST_IP_FILE="/tmp/hetzner-dns-ha-ip"
|
||||
INTERFACE="enp3s0"
|
||||
|
||||
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
|
||||
|
||||
LAST_IP=""
|
||||
if [[ -f "$LAST_IP_FILE" ]]; then
|
||||
LAST_IP=$(cat "$LAST_IP_FILE")
|
||||
fi
|
||||
|
||||
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
|
||||
echo "IP unchanged, NOOP update."
|
||||
exit 0
|
||||
else
|
||||
echo "Updating IP"
|
||||
|
||||
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
|
||||
|
||||
curl \
|
||||
--fail \
|
||||
-X POST \
|
||||
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$JSON_BODY" \
|
||||
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/ha/A/actions/set_records" \
|
||||
&& echo $CURRENT_IP > $LAST_IP_FILE
|
||||
fi
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user