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:
+33
-4
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"custom/fan": {
|
||||
"custom/cooling_pad": {
|
||||
"return-type": "json",
|
||||
"exec": "~/.config/waybar/scripts/fan.py",
|
||||
"interval": 5,
|
||||
|
||||
+26
-3
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user