Network Topology & NAT
homectl is engineered to interact with IoT hardware on local private subnets (10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16). Certain IoT protocols introduce specific routing considerations.
Inbound vs. Outbound Protocol Flows
Section titled “Inbound vs. Outbound Protocol Flows”| Device / Protocol | Inbound or Outbound | Port | Behavior |
|---|---|---|---|
| Lutron LEAP | Outbound | 8081 (TCP) | Host connects directly to bridge over TLS. Reconnects automatically on network drops. |
| Sonos Control | Outbound | 1400 (TCP) | Host sends standard HTTP SOAP requests to the speaker. |
| Sonos Events (GENA) | Inbound | Dynamic (TCP) | Speaker makes an inbound HTTP NOTIFY request back to homectl. |
| Google Cast | Outbound | 8009 (TCP) | TLS connection initiated to Cast device. |
| RTSP Cameras | Outbound | 554 (TCP) | TCP connection initiated to camera RTSP server. |
| Qolsys Panel | Outbound | 12345 (TCP) | WebSocket client connects to panel. |
The Inbound NAT Challenge (GENA Events)
Section titled “The Inbound NAT Challenge (GENA Events)”Because Sonos uses UPnP GENA, events are pushed via incoming HTTP calls:
[homectl] ──( SUBSCRIBE )──> [Sonos Speaker][homectl] <──( NOTIFY )─────── [Sonos Speaker]When running homectl inside a NATed development environment (such as a Docker container, WSL2, or ChromeOS Crostini):
- The speaker cannot reach container/VM internal IPs (e.g.,
100.115.x.xor172.17.x.x). - The speaker issues an HTTP NOTIFY that fails with
412 Precondition Failedor times out.
Solutions:
Section titled “Solutions:”- Set
callback_ipinconfig.json: Set this to your host’s actual LAN IP on the physical router. - Reverse SSH Tunneling:
Forward the dynamic GENA port through your host machine to your development container:
Terminal window ssh -R 37915:localhost:37915 user@192.168.1.100 - Diagnostic Utility:
The repository includes
tools/gena_debug.goto test whether a Sonos speaker can successfully complete a handshake with your callback URL:Terminal window go run tools/gena_debug.go -ip 192.168.1.120 -callback-ip 192.168.1.100 -port 37915