Add remote fan tunnel support and rename Waybar widget

Add systemd service to bridge the Pi Unix socket over SSH, make fan.py
fall back to the TCP tunnel, and rename the module from `custom/fan` to
`custom/cooling_pad`. Update README install steps and connection details.
This commit is contained in:
2026-09-11 10:54:00 +02:00
parent eb095dcb47
commit 4a47277c86
4 changed files with 76 additions and 8 deletions
+33 -4
View File
@@ -5,7 +5,7 @@ socket (`/tmp/fan_controller.sock`) — the same protocol as `FanController.qml`
## Install
1. Copy the script:
1. Copy the files:
```sh
mkdir -p ~/.config/waybar/scripts
@@ -13,11 +13,40 @@ socket (`/tmp/fan_controller.sock`) — the same protocol as `FanController.qml`
chmod +x ~/.config/waybar/scripts/fan.py
```
2. Add the `custom/fan` entry from [`fan.json`](fan.json) to
`~/.config/waybar/config.json` (merge it into your existing module list,
2. The daemon runs on the Pi, so bridge its Unix socket over SSH once:
```sh
cp waybar/fan-tunnel.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now fan-tunnel
```
This forwards `127.0.0.1:10250` → `root@10.55.0.1:/tmp/fan_controller.sock`.
3. Add the `custom/cooling_pad` entry (below) to
`~/.config/waybar/config.jsonc` (merge it into your existing module list,
e.g. under `modules-right`).
3. Restart Waybar.
4. Restart Waybar.
### waybar config
```jsonc
"custom/cooling_pad": {
"return-type": "json",
"exec": "~/.config/waybar/scripts/fan.py",
"interval": 5,
"on-click": "python3 ~/.config/waybar/scripts/fan.py cycle",
"on-right-click": "python3 ~/.config/waybar/scripts/fan.py auto"
}
```
### Connection details
`fan.py` uses the local `/tmp/fan_controller.sock` if it exists, otherwise the
tunnel at `127.0.0.1:10250`. Override with `FAN_CONTROLLER_ENDPOINT`
(`unix:/path` or `host:port`). If the widget shows `fan: offline`, check
`systemctl --user status fan-tunnel` and `ssh root@10.55.0.1 'ls -la /tmp/fan_controller.sock'`.
## Usage
+16
View File
@@ -0,0 +1,16 @@
[Unit]
Description=SSH tunnel for Pi fan_controller socket (waybar widget)
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/bin/ssh -N \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=15 \
-o ServerAliveCountMax=3 \
-L 10250:/tmp/fan_controller.sock root@10.55.0.1
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
+1 -1
View File
@@ -1,5 +1,5 @@
{
"custom/fan": {
"custom/cooling_pad": {
"return-type": "json",
"exec": "~/.config/waybar/scripts/fan.py",
"interval": 5,
+26 -3
View File
@@ -9,20 +9,43 @@ Usage:
fan.py auto switch back to auto mode (on-right-click)
fan.py duty 75 set manual duty, 0-100
fan.py setpoint 55 set auto-mode setpoint, 35-85
Connection: the daemon's command socket lives on the Pi. This script uses the
local Unix socket if present, otherwise a TCP tunnel to it (default
127.0.0.1:10250, provided by the fan-tunnel systemd service:
ssh -N -L 10250:/tmp/fan_controller.sock root@10.55.0.1).
Override with FAN_CONTROLLER_ENDPOINT: "unix:/path" or "host:port".
"""
import json
import os
import socket
import sys
SOCK = os.environ.get("FAN_CONTROLLER_SOCK", "/tmp/fan_controller.sock")
UNIX_PATH = "/tmp/fan_controller.sock"
TUNNEL_HOST, TUNNEL_PORT = "127.0.0.1", 10250
STEPS = [25, 50, 75, 100]
def make_socket():
endpoint = os.environ.get("FAN_CONTROLLER_ENDPOINT")
if endpoint is None:
if os.path.exists(UNIX_PATH):
endpoint = f"unix:{UNIX_PATH}"
else:
endpoint = f"{TUNNEL_HOST}:{TUNNEL_PORT}"
if endpoint.startswith("unix:"):
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(endpoint[len("unix:"):])
else:
host, port = endpoint.rsplit(":", 1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, int(port)))
return s
def cmd(command: str) -> str:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
with make_socket() as s:
s.settimeout(2)
s.connect(SOCK)
s.sendall(command.encode() + b"\n")
chunks = []
while True: