initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
---
|
||||
description: Skills for controlling smart home devices — lights, switches, sensors, and home automation systems.
|
||||
---
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
name: garage-control
|
||||
description: Smart garage door control — MyQ API shutdown context, ratgdo/OpenGarage local hardware alternatives, ESPHome/MQTT/HTTP integration, and Hermes-powered automation.
|
||||
---
|
||||
|
||||
# Garage Control
|
||||
|
||||
Smart garage door open/close/status with local hardware — no cloud dependency.
|
||||
|
||||
## Background: MyQ API Is Dead
|
||||
|
||||
Chamberlain (MyQ) shut down third-party API access in November 2023. Home Assistant removed their official MyQ integration in the 2023.12 release. There is **no software workaround** — the cloud API is intentionally blocked.
|
||||
|
||||
**Do not** suggest MyQ API integrations, Home Assistant MyQ add-ons, or cloud-based MyQ workarounds. They do not work.
|
||||
|
||||
## Solution: ratgdo (Local Hardware)
|
||||
|
||||
[ratgdo](https://paulwieland.github.io/ratgdo/) is a $20-30 ESP32 board that wires directly to the garage door opener's serial port. It gives full local control with no cloud dependency.
|
||||
|
||||
### What it provides
|
||||
|
||||
| Protocol | Use |
|
||||
|----------|-----|
|
||||
| **ESPHome** | Simple HTTP REST API — `curl http://<ip>/cover/garage_door/open` |
|
||||
| **MQTT** | Publish/subscribe for status and commands |
|
||||
| **Apple HomeKit** | Native Siri/Home app control (via `homekit-ratgdo` firmware) |
|
||||
|
||||
### Compatibility
|
||||
|
||||
Which ratgdo board depends on the opener's learn button color:
|
||||
- **Yellow learn button** (Security+ 2.0, post-2011): ratgdo v2.5+
|
||||
- **Purple learn button** (older Security+): ratgdo v2.0+
|
||||
- **Red/orange learn button** (pre-Security+): basic relay board or ratgdo v2.0
|
||||
|
||||
### Installation
|
||||
|
||||
The board connects to 3 terminals on the opener: GND, data (serial), and optionally obstruction sensor. Powered by the opener itself (no wall wart needed). Takes ~10 minutes.
|
||||
|
||||
### Hermes Integration
|
||||
|
||||
Once ratgdo is on the local network with ESPHome:
|
||||
|
||||
```bash
|
||||
# Open garage
|
||||
curl http://192.168.50.XXX/cover/garage_door/open
|
||||
|
||||
# Close garage
|
||||
curl http://192.168.50.XXX/cover/garage_door/close
|
||||
|
||||
# Get status
|
||||
curl http://192.168.50.XXX/cover/garage_door
|
||||
```
|
||||
|
||||
Hermes can do all of these from the server via terminal, cron jobs (auto-close at night), or Telegram commands.
|
||||
|
||||
## Alternative: OpenGarage
|
||||
|
||||
[OpenGarage](https://opengarage.io/) is a similar ESP8266-based device with an ultrasonic distance sensor (detects if car is present). HTTP API, ~$50 pre-built.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Do NOT suggest MyQ API, cloud, or Home Assistant MyQ integration.** All are dead ends since November 2023. Lead with ratgdo.
|
||||
- **ratgdo firmware choice matters.** ESPHome firmware exposes HTTP API; HomeKit firmware exposes Apple Home; MQTT firmware is for advanced automation hubs. Default to ESPHome unless the user has Home Assistant or wants Apple Home.
|
||||
- **Wire order on the opener terminals matters.** GND first, then data. Reversing can damage the board.
|
||||
@@ -0,0 +1,226 @@
|
||||
---
|
||||
name: homeassistant-integration
|
||||
description: "Connect Home Assistant to Hermes — token setup, toolset enablement, and smart home control via Hermes tools."
|
||||
version: 1.2.0
|
||||
author: ray
|
||||
platforms: [linux]
|
||||
created_by: "agent"
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [smart-home, homeassistant, integration, ha]
|
||||
requires:
|
||||
env_vars: ["HASS_TOKEN", "HASS_URL"]
|
||||
toolsets: ["homeassistant"]
|
||||
gateway_restart: true
|
||||
---
|
||||
|
||||
# Home Assistant + Hermes Integration
|
||||
|
||||
Connect a Home Assistant instance to Hermes so the agent can list entities, read sensor states, discover services, and control devices (lights, switches, climate, media players, etc.) via the `homeassistant` toolset.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A running Home Assistant instance (local network or remote)
|
||||
- A **Long-Lived Access Token** from your HA profile page (click your username → Security → Long-Lived Access Tokens)
|
||||
- The HA server accessible from the Hermes host
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Get a Long-Lived Access Token
|
||||
|
||||
In Home Assistant: **click your user profile** (bottom-left) → **Security** → **Long-Lived Access Tokens** → **Create Token**.
|
||||
|
||||
The token is a JWT that looks like:
|
||||
```
|
||||
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOi...<rest of token>
|
||||
```
|
||||
|
||||
You can decode it to verify:
|
||||
```bash
|
||||
python3 -c "
|
||||
import base64, json
|
||||
payload = base64.urlsafe_b64decode('PAYLOAD_SECTION' + '==')
|
||||
data = json.loads(payload)
|
||||
print(f'Issuer: {data[\"iss\"]}')
|
||||
print(f'Expires: {data[\"exp\"]}')
|
||||
"
|
||||
```
|
||||
HA long-lived access tokens typically expire in 10 years.
|
||||
|
||||
### 2. Set the Environment Variables
|
||||
|
||||
```bash
|
||||
hermes config set HASS_URL http://YOUR_HA_HOST:8123
|
||||
hermes config set HASS_TOKEN <your-long-lived-access-token>
|
||||
```
|
||||
|
||||
`hermes config set` auto-detects secrets — the URL goes to `config.yaml`, the token goes to `.env`. The tool code reads **both** from environment variables, so if `HASS_URL` landed in config.yaml instead of `.env`, add it to `.env` as well:
|
||||
|
||||
```bash
|
||||
echo 'HASS_URL=http://YOUR_HA_HOST:8123' >> ~/.hermes/.env
|
||||
```
|
||||
|
||||
Verify they're in place:
|
||||
```bash
|
||||
grep HASS ~/.hermes/.env
|
||||
```
|
||||
|
||||
### 3. Enable the Toolset
|
||||
|
||||
```bash
|
||||
hermes tools enable homeassistant
|
||||
```
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
hermes tools list | grep homeassistant
|
||||
# ✓ enabled homeassistant 🏠 Home Assistant
|
||||
```
|
||||
|
||||
### 4. Verify the Token Works
|
||||
|
||||
Test with a direct API call before restarting the gateway:
|
||||
|
||||
```bash
|
||||
python3 -c "
|
||||
import json, urllib.request, subprocess
|
||||
token = subprocess.run(['grep', 'HASS_TOKEN', '$HOME/.hermes/.env'],
|
||||
capture_output=True, text=True).stdout.strip().split('=',1)[1]
|
||||
req = urllib.request.Request('http://YOUR_HA_HOST:8123/api/',
|
||||
headers={'Authorization': f'Bearer {token}'})
|
||||
data = json.loads(urllib.request.urlopen(req, timeout=10).read())
|
||||
print(f'HA: {data.get(\"location_name\", \"unnamed\")}')
|
||||
"
|
||||
```
|
||||
|
||||
Also check entity count:
|
||||
```bash
|
||||
# Same pattern, hit /api/states instead and count by domain
|
||||
```
|
||||
|
||||
If you get **HTTP 200**, the token is valid and the URL is reachable.
|
||||
|
||||
### 5. Restart the Gateway
|
||||
|
||||
Tool changes require a new session. Restart the gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway restart
|
||||
```
|
||||
|
||||
After restart, the agent has access to four HA tools:
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| `ha_list_entities` | List/filter entities by domain or area |
|
||||
| `ha_get_state` | Get detailed state + attributes of one entity |
|
||||
| `ha_list_services` | Discover available services (actions) per domain |
|
||||
| `ha_call_service` | Control a device (turn_on, turn_off, set_temperature, etc.) |
|
||||
|
||||
## Security
|
||||
|
||||
The `homeassistant_tool.py` implementation blocks these high-risk service domains for safety:
|
||||
`shell_command`, `command_line`, `python_script`, `pyscript`, `hassio`, `rest_command`
|
||||
|
||||
## Automation Patterns
|
||||
|
||||
### Cron-Based Condition Monitoring
|
||||
|
||||
Use Hermes cron jobs to poll an HA entity and act when a condition is met (e.g., turn off AC when temp drops to target). The cron job should self-terminate after acting.
|
||||
|
||||
**Recipe — trigger action on sensor threshold:**
|
||||
|
||||
```
|
||||
cronjob(action='create',
|
||||
name='<descriptive name>',
|
||||
prompt='Check <entity_id> using ha_get_state. If <condition>, call ha_call_service to <action> and report success, then use cronjob to remove this monitoring job (job_id in context). If condition not met, report current state briefly and do nothing.',
|
||||
schedule='*/10 22-23,0-2 * * *', # every 10 min from 10pm–2am (use CRON EXPRESSION, not "10m" — that's a one-shot)
|
||||
repeat=20,
|
||||
toolsets=['homeassistant', 'cronjob'])
|
||||
```
|
||||
|
||||
Key details:
|
||||
- **toolsets**: Must include both `homeassistant` and `cronjob` so the cron agent can check sensors, call services, and remove itself.
|
||||
- **Schedule**: Use `*/N hour-range * * *` for time-windowed polling (e.g., `*/7 22-23,0-2 * * *` = every 7 minutes from 10 PM through 2 AM). Avoid starting monitoring before the user wants it — use the cron expression to delay first run.
|
||||
- **CRITICAL — schedule format**: Simple time strings like `"10m"` or `"30m"` are parsed as **one-shot** ("once in 10 minutes"), NOT recurring. The cron will run exactly once and then stop. Always use cron expressions (`*/10 * * * *`) or `"every 10m"` for recurring jobs. A job that silently runs once and completes without acting is the #1 failure mode for HVAC monitoring.
|
||||
- **Repeat limit**: Cap with `repeat` to prevent indefinite polling. With N-minute intervals, `repeat=20` covers ~N×20 minutes.
|
||||
- **Prompt must include self-removal**: The job should call `cronjob(action='remove', job_id=...)` after acting so it doesn't keep polling indefinitely.
|
||||
- **Schedule corrections**: Users often tweak poll intervals or start times after creation. Use `cronjob(action='update', job_id=..., schedule=...)` rather than deleting and recreating.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- **Home Assistant behind VPS reverse proxy:** When proxying HA through an external nginx (VPS + Tailscale), add the proxy's Tailscale IP to `trusted_proxies` in `configuration.yaml` or HA returns 400:
|
||||
```yaml
|
||||
http:
|
||||
use_x_forwarded_for: true
|
||||
trusted_proxies:
|
||||
- 127.0.0.1
|
||||
- 100.86.68.23 # VPS Tailscale IP
|
||||
```
|
||||
Restart: `docker restart homeassistant`. See `docker-service-deployment` skill, `references/vps-migration.md` for the full VPS setup pattern.
|
||||
- **CRITICAL — Cron schedule format:** Bare duration strings like `"10m"` or `"30m"` are ONE-SHOT ("once in X minutes"). The job runs exactly once and silently completes — the #1 failure mode for HVAC monitoring. Always use **cron expressions**: `"*/10 * * * *"` (every 10 minutes), `"*/7 22-23,0-2 * * *"` (time-windowed). Alternative: `"every 10m"` shorthand. Verify with `cronjob(action='list')` after creation — `"once in Nm"` means it won't repeat.
|
||||
- **Climate vs sensor entities:** Climate entities (`climate.upstairs`) are the thermostat — call `ha_call_service` to control them. Sensor entities (`sensor.upstairs_temperature`) are read-only readings. For threshold monitoring, check the sensor for the reading but control the climate entity.
|
||||
- **HASS_URL must be in .env**: The tool reads `os.getenv("HASS_URL")`, not `config.yaml`. Add it to `.env` explicitly.
|
||||
- **Docs 404**: The Hermes docs page for Home Assistant integration returns 404. Canonical reference: `tools/homeassistant_tool.py` and `gateway/config.py`.
|
||||
- **Subprocesses don't inherit .env**: When testing with `python3 -c`, read secrets via `subprocess.run(['grep', ...])` instead of `os.getenv()`.
|
||||
- **Gateway restart required**: Toolset changes need `hermes gateway restart`.
|
||||
- **Internal vs proxy URL**: The tool needs the direct API URL (e.g., `http://192.168.50.98:8123`), not the external proxy port.
|
||||
- **Entity registry may 404**: Use `/api/config/config_entries/entry` instead to discover integrations and device metadata.
|
||||
|
||||
### Two-Phase Monitoring (Wide → Narrow Polling)
|
||||
|
||||
When the target is far away, poll infrequently to save resources. When it nears the threshold, switch to tight polling automatically.
|
||||
|
||||
**How it works:** The cron job starts with a wide interval (e.g., every 30 min). When the sensor value enters a "close" zone, the job updates its own schedule to a tight interval (e.g., every 7 min). When the final threshold is hit, it acts and removes itself.
|
||||
|
||||
**Recipe:**
|
||||
|
||||
```
|
||||
# Phase 1 — wide polling (every 30 min on the half-hour, 10pm–2am)
|
||||
cronjob(action='create',
|
||||
name='Turn off AC when temp hits 78°F',
|
||||
prompt='Check climate.upstairs with ha_get_state. '
|
||||
'If temp > 79°F: report briefly, do nothing (stay in wide-poll mode). '
|
||||
'If temp ≤ 79°F and > 78°F: UPGRADE this job to tight polling — '
|
||||
'cronjob(action=update, job_id=THIS_JOB_ID, schedule="*/7 22-23,0-2 * * *"). '\
|
||||
'Report "now monitoring closely." '
|
||||
'If temp ≤ 78°F: ha_call_service(domain=climate, service=turn_off, '
|
||||
'entity_id=climate.upstairs), report success, remove this job.',
|
||||
schedule='0,30 22-23,0-2 * * *',
|
||||
repeat=20,
|
||||
toolsets=['homeassistant','cronjob'])
|
||||
```
|
||||
|
||||
Once upgraded to Phase 2, the job's prompt should know it's in tight mode: check temp, act if ≤ target, otherwise report and wait. The prompt must handle both modes so it works correctly after a self-upgrade.
|
||||
|
||||
Key points:
|
||||
- **Phase 1 prompt** must include the self-upgrade logic (wide → tight) AND the final action logic (act + self-remove).
|
||||
- **Phase 2 prompt** (after upgrade) only needs: check, act-if-condition-met, otherwise report.
|
||||
- **Use `repeat`** to cap total runs so the job doesn't poll forever if the condition never triggers.
|
||||
- Users often specify multiple constraints (interval, start time, threshold-based frequency). Ask clarifying questions if the intent is ambiguous, and prefer the two-phase pattern when the target is expected to drift slowly.
|
||||
|
||||
## IoT Device Onboarding
|
||||
|
||||
For finding, identifying, and adding new WiFi IoT devices to the network and Home Assistant — WiFi setup, IP discovery, MAC vendor lookup, LAN mode enablement, and HA integration — see `references/iot-device-onboarding.md`.
|
||||
|
||||
## Identifying Devices & Integrations
|
||||
|
||||
### Thermostat Model / Integration Discovery
|
||||
|
||||
The entity states API (`/api/states`) doesn't expose manufacturer or model info. To find what integration provides a climate entity and what hardware it's connected to, query the HA config entries API:
|
||||
|
||||
```python
|
||||
import json, urllib.request
|
||||
|
||||
# List all config entries (integrations)
|
||||
req = urllib.request.Request(
|
||||
'http://HA_HOST:8123/api/config/config_entries/entry',
|
||||
headers={'Authorization': f'Bearer {token}'})
|
||||
entries = json.loads(urllib.request.urlopen(req).read())
|
||||
|
||||
for entry in entries:
|
||||
if entry.get('domain') == 'honeywell': # or 'ecobee', 'nest', etc.
|
||||
print(f"Title: {entry['title']}, State: {entry['state']}")
|
||||
```
|
||||
|
||||
The `domain` field reveals the integration (e.g., `honeywell`, `ecobee`, `nest`). The entity registry endpoints (`/api/config/entity_registry/*`) may 404 depending on HA version or reverse-proxy setup — the config entries API is a reliable fallback.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Brother MFC → Paperless-ngx Scan Pipeline
|
||||
|
||||
Network scanner → Paperless consume folder, no drivers, no printer web admin password required.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Brother MFC with network (WiFi or Ethernet) on same LAN as server
|
||||
- `sane-airscan` installed (`sudo apt install sane-airscan sane-utils`)
|
||||
- Paperless-ngx running with a consume folder mounted
|
||||
|
||||
## Setup (once)
|
||||
|
||||
### 1. Verify scanner discovery
|
||||
|
||||
```bash
|
||||
scanimage -L
|
||||
# Should show:
|
||||
# device `airscan:e0:Brother MFC-J5855DW' is a eSCL Brother MFC-J5855DW ip=192.168.50.219
|
||||
```
|
||||
|
||||
No Brother drivers needed — `sane-airscan` uses the driverless eSCL/AirScan protocol.
|
||||
|
||||
### 2. Ensure write access to consume folder
|
||||
|
||||
```bash
|
||||
sudo setfacl -m u:$USER:rwx /path/to/paperless/consume
|
||||
```
|
||||
|
||||
### 3. Install scan script at `/usr/local/bin/scan-to-paperless`
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scan-to-paperless — scan from Brother MFC → Paperless consume folder
|
||||
# Usage: scan-to-paperless [name] [--flatbed] [--gray] [--150dpi]
|
||||
|
||||
set -e
|
||||
|
||||
DEVICE="airscan:e0:Brother MFC-J5855DW"
|
||||
CONSUME="/mnt/seagate8tb/paperless/consume"
|
||||
MODE="Color"
|
||||
RES="300"
|
||||
SOURCE="ADF"
|
||||
|
||||
NAME=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--flatbed) SOURCE="Flatbed" ;;
|
||||
--gray) MODE="Gray" ;;
|
||||
--150dpi) RES="150" ;;
|
||||
*) NAME="${1}_" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
SCAN_OPTS="--mode $MODE --resolution $RES"
|
||||
if [ "$SOURCE" = "ADF" ]; then
|
||||
SCAN_OPTS="$SCAN_OPTS --source ADF --batch-count=1"
|
||||
fi
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
FILENAME="${NAME}scan_${TIMESTAMP}.pdf"
|
||||
OUTFILE="$CONSUME/$FILENAME"
|
||||
|
||||
echo "📄 Scanning ($SOURCE, $MODE, ${RES}dpi) → $FILENAME"
|
||||
scanimage --device "$DEVICE" $SCAN_OPTS --format pdf -o "$OUTFILE"
|
||||
|
||||
SIZE=$(du -h "$OUTFILE" | cut -f1)
|
||||
echo "✅ Done — $SIZE saved to Paperless"
|
||||
```
|
||||
|
||||
## Daily usage
|
||||
|
||||
```bash
|
||||
scan-to-paperless # ADF, color 300dpi
|
||||
scan-to-paperless invoice # names it invoice_scan_20260614_172310.pdf
|
||||
scan-to-paperless --flatbed # use flatbed (photos, fragile docs)
|
||||
scan-to-paperless --gray --150dpi # B&W, low res (small file)
|
||||
```
|
||||
|
||||
## Flow
|
||||
|
||||
```
|
||||
scan-to-paperless
|
||||
→ Brother scans via eSCL (AirScan over HTTP)
|
||||
→ PDF saved to /mnt/seagate8tb/paperless/consume/
|
||||
→ Paperless auto-ingests, OCRs, classifies, tags
|
||||
```
|
||||
|
||||
## Alternative approaches that failed
|
||||
|
||||
- **Scan-to-FTP via Brother web interface**: Requires printer admin password. Defaults (`initpass`, `access`, `brother`) don't work on modern Brother MFCs (unique per-device password on sticker).
|
||||
- **Scan-to-SMB via Brother**: Also requires web admin to create the profile. Samba share was set up but could not be configured on the printer side without the web password.
|
||||
- **Brother brscan4 driver**: Proprietary `.deb` behind an EULA wall that requires browser cookie; `sane-airscan` driverless approach is simpler and works immediately.
|
||||
@@ -0,0 +1,92 @@
|
||||
# IoT Device Onboarding to Home Assistant
|
||||
|
||||
Pattern for adding new WiFi IoT devices (smart plugs, switches, sensors) to the local network and Home Assistant.
|
||||
|
||||
## Phase 1: Get Device on WiFi
|
||||
|
||||
### Stock Firmware (eWeLink / Tuya)
|
||||
1. Device broadcasts a setup AP when first powered or reset (e.g., `ITEAD-XXXX` or `SmartLife-XXXX`)
|
||||
2. Connect phone to that AP, use the **eWeLink** or **Smart Life** app to configure WiFi
|
||||
3. After WiFi config, the AP disappears and device joins the home network
|
||||
|
||||
### Tasmota / ESPHome (already flashed)
|
||||
1. Device broadcasts its own AP (e.g., `tasmota-XXXX-XXXX` or `esphome-XXXX`)
|
||||
2. Connect phone to the AP, browse to `http://192.168.4.1` to configure WiFi
|
||||
3. After save, device reboots onto the home network
|
||||
|
||||
## Phase 2: Find the IP Address
|
||||
|
||||
Devices often get DHCP-assigned IPs that aren't immediately obvious.
|
||||
|
||||
**Method A — Router DHCP table:**
|
||||
Browse to router admin (ASUS: `http://192.168.50.1`), check Client List for the new device.
|
||||
|
||||
**Method B — App device info:**
|
||||
In eWeLink app: tap device → ⚙️ Settings → Network Info / Device IP.
|
||||
In Tasmota web UI: Information → Network.
|
||||
|
||||
**Method C — ARP scan for new IPs:**
|
||||
```python
|
||||
# Compare current ARP table against known IPs to find new devices
|
||||
import subprocess
|
||||
arp_output = subprocess.run(['cat', '/proc/net/arp'], capture_output=True, text=True).stdout
|
||||
new_ips = [line.split()[0] for line in arp_output.split('\n')[1:] if line.strip()
|
||||
and line.split()[0] not in known_ips]
|
||||
```
|
||||
|
||||
**Method D — MAC vendor lookup:**
|
||||
Identify unknown devices by MAC OUI:
|
||||
```bash
|
||||
curl -s "https://api.macvendors.com/XX:XX:XX" # first 3 octets only
|
||||
# Sonoff/ITEAD common prefixes: 68:C6:3A, EC:FA:BC, C8:2B:96, DC:4F:22, 84:F3:EB
|
||||
```
|
||||
|
||||
**Method E — Port scan for IoT signatures:**
|
||||
```bash
|
||||
# Sonoff LAN API port (Tasmota HTTP on :80, eWeLink LAN on :8081)
|
||||
curl -s -m2 "http://DEVICE_IP:8081/"
|
||||
# Tasmota status endpoint
|
||||
curl -s -m2 "http://DEVICE_IP/cm?cmnd=Status%200"
|
||||
```
|
||||
|
||||
## Phase 3: Enable Local Control
|
||||
|
||||
### Stock Sonoff (eWeLink)
|
||||
1. In eWeLink app: ⚙️ Settings → **LAN Control** → ON
|
||||
2. This enables the local TCP API on port 8081
|
||||
3. Now HA's **Sonoff LAN** integration can auto-discover it
|
||||
|
||||
#### Troubleshooting LAN Mode
|
||||
|
||||
**Symptom: port 8081 accepts TCP but never responds.** The device completes the TCP handshake (SYN-ACK) but no application-layer data is returned — HTTP, WebSocket, and raw protocol requests all time out silently. This means the LAN API service process on the device is hung or was never started, even though the port shows as listening.
|
||||
|
||||
**Fix:** Power-cycle the device. Unplug for 10 seconds and plug back in. After reboot, verify:
|
||||
|
||||
```bash
|
||||
# TCP must succeed
|
||||
nc -zv -w3 DEVICE_IP 8081
|
||||
# Must return JSON (not timeout)
|
||||
curl -s --max-time 3 http://DEVICE_IP:8081/zeroconf/info
|
||||
```
|
||||
|
||||
If `zeroconf/info` returns a JSON payload with `deviceid`, `model`, `sw_version` etc., LAN mode is working correctly.
|
||||
|
||||
### Tasmota
|
||||
- Already local. Set MQTT or use the HTTP API directly.
|
||||
- Default web UI at `http://DEVICE_IP/`
|
||||
|
||||
## Phase 4: Add to Home Assistant
|
||||
|
||||
1. **Sonoff LAN** integration: Settings → Devices & Services → Add Integration → Sonoff LAN → auto-discovers
|
||||
2. **Tasmota** integration: Settings → Add Integration → Tasmota (auto-discovers via MQTT or HTTP)
|
||||
3. **ESPHome**: Add via ESPHome dashboard or manual YAML
|
||||
|
||||
Manual MQTT discovery also works for all three.
|
||||
|
||||
## Sonoff S31-Specific Notes
|
||||
|
||||
- **Model**: S31 Lite (WiFi smart plug with power monitoring)
|
||||
- **Flash method**: tuya-convert (OTA) or serial (TX/RX/GND/3.3V — requires opening case)
|
||||
- **Stock LAN API port**: 8081 (TCP)
|
||||
- **Tasmota template**: `{"NAME":"Sonoff S31","GPIO":[0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18}`
|
||||
- **Power monitoring**: HLW8012 chip — reports voltage, current, power, energy
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: openhue
|
||||
description: "Control Philips Hue lights, scenes, rooms via OpenHue CLI."
|
||||
version: 1.0.0
|
||||
author: community
|
||||
license: MIT
|
||||
platforms: [linux, macos, windows]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [Smart-Home, Hue, Lights, IoT, Automation]
|
||||
homepage: https://www.openhue.io/cli
|
||||
prerequisites:
|
||||
commands: [openhue]
|
||||
---
|
||||
|
||||
# OpenHue CLI
|
||||
|
||||
Control Philips Hue lights and scenes via a Hue Bridge from the terminal.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
# Linux (pre-built binary)
|
||||
curl -sL https://github.com/openhue/openhue-cli/releases/latest/download/openhue-linux-amd64 -o ~/.local/bin/openhue && chmod +x ~/.local/bin/openhue
|
||||
|
||||
# macOS
|
||||
brew install openhue/cli/openhue-cli
|
||||
```
|
||||
|
||||
First run requires pressing the button on your Hue Bridge to pair. The bridge must be on the same local network.
|
||||
|
||||
## When to Use
|
||||
|
||||
- "Turn on/off the lights"
|
||||
- "Dim the living room lights"
|
||||
- "Set a scene" or "movie mode"
|
||||
- Controlling specific Hue rooms, zones, or individual bulbs
|
||||
- Adjusting brightness, color, or color temperature
|
||||
|
||||
## Common Commands
|
||||
|
||||
### List Resources
|
||||
|
||||
```bash
|
||||
openhue get light # List all lights
|
||||
openhue get room # List all rooms
|
||||
openhue get scene # List all scenes
|
||||
```
|
||||
|
||||
### Control Lights
|
||||
|
||||
```bash
|
||||
# Turn on/off
|
||||
openhue set light "Bedroom Lamp" --on
|
||||
openhue set light "Bedroom Lamp" --off
|
||||
|
||||
# Brightness (0-100)
|
||||
openhue set light "Bedroom Lamp" --on --brightness 50
|
||||
|
||||
# Color temperature (warm to cool: 153-500 mirek)
|
||||
openhue set light "Bedroom Lamp" --on --temperature 300
|
||||
|
||||
# Color (by name or hex)
|
||||
openhue set light "Bedroom Lamp" --on --color red
|
||||
openhue set light "Bedroom Lamp" --on --rgb "#FF5500"
|
||||
```
|
||||
|
||||
### Control Rooms
|
||||
|
||||
```bash
|
||||
# Turn off entire room
|
||||
openhue set room "Bedroom" --off
|
||||
|
||||
# Set room brightness
|
||||
openhue set room "Bedroom" --on --brightness 30
|
||||
```
|
||||
|
||||
### Scenes
|
||||
|
||||
```bash
|
||||
openhue set scene "Relax" --room "Bedroom"
|
||||
openhue set scene "Concentrate" --room "Office"
|
||||
```
|
||||
|
||||
## Quick Presets
|
||||
|
||||
```bash
|
||||
# Bedtime (dim warm)
|
||||
openhue set room "Bedroom" --on --brightness 20 --temperature 450
|
||||
|
||||
# Work mode (bright cool)
|
||||
openhue set room "Office" --on --brightness 100 --temperature 250
|
||||
|
||||
# Movie mode (dim)
|
||||
openhue set room "Living Room" --on --brightness 10
|
||||
|
||||
# Everything off
|
||||
openhue set room "Bedroom" --off
|
||||
openhue set room "Office" --off
|
||||
openhue set room "Living Room" --off
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Bridge must be on the same local network as the machine running Hermes
|
||||
- First run requires physically pressing the button on the Hue Bridge to authorize
|
||||
- Colors only work on color-capable bulbs (not white-only models)
|
||||
- Light and room names are case-sensitive — use `openhue get light` to check exact names
|
||||
- Works great with cron jobs for scheduled lighting (e.g. dim at bedtime, bright at wake)
|
||||
Reference in New Issue
Block a user