Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bed99cc4c |
@@ -16,7 +16,7 @@ You need:
|
|||||||
- An already provisioned Deebot N95 using the `wukong` / class `155` protocol captured for this project. Other Ecovacs models are not established as compatible.
|
- An already provisioned Deebot N95 using the `wukong` / class `155` protocol captured for this project. Other Ecovacs models are not established as compatible.
|
||||||
- A Docker host (or a machine running the Go binary) with a **stable LAN IPv4 address** reachable from the robot. Reserve its address in DHCP.
|
- A Docker host (or a machine running the Go binary) with a **stable LAN IPv4 address** reachable from the robot. Reserve its address in DHCP.
|
||||||
- Control of the DNS resolver supplied to the robot by DHCP. It must answer `lbo.ecouser.net` with that LAN address. A direct A record is sufficient. The bridge does not provide DNS. The XMPP JID domain `155.ecorobot.net` does not need a DNS override for this captured firmware.
|
- Control of the DNS resolver supplied to the robot by DHCP. It must answer `lbo.ecouser.net` with that LAN address. A direct A record is sufficient. The bridge does not provide DNS. The XMPP JID domain `155.ecorobot.net` does not need a DNS override for this captured firmware.
|
||||||
- TCP **8007**, **8005**, and **5223** reachable from the robot at that address. TCP **8080** is the optional health endpoint; it need not be open to the robot.
|
- TCP **8007**, **8005**, and **5223** reachable from the robot at that address. TCP **8080** is the optional health endpoint; it need not be open to the robot. If that port is already taken (for example OpenThread Border Router on Home Assistant OS), the process logs the bind error and keeps the robot listeners running. Set `HEALTH_PORT` to a free port if you still want `/healthz`.
|
||||||
- An MQTT broker reachable from the bridge and Home Assistant configured to use that broker, with MQTT discovery enabled. Its discovery prefix must match `HA_DISCOVERY_PREFIX` (normally `homeassistant`).
|
- An MQTT broker reachable from the bridge and Home Assistant configured to use that broker, with MQTT discovery enabled. Its discovery prefix must match `HA_DISCOVERY_PREFIX` (normally `homeassistant`).
|
||||||
- A correct host clock and local timezone. The bridge sends the robot the current time and the process's local UTC offset on each session.
|
- A correct host clock and local timezone. The bridge sends the robot the current time and the process's local UTC offset on each session.
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ Only `ADVERTISE_IP` and `MQTT_HOST` are required. An absent variable uses its de
|
|||||||
| `PORT_LOOKUP` | `8007` | HTTP `POST /lookup.do` for `EcoMsgNew` and `EcoUpdate`. |
|
| `PORT_LOOKUP` | `8007` | HTTP `POST /lookup.do` for `EcoMsgNew` and `EcoUpdate`. |
|
||||||
| `PORT_FIRMWARE` | `8005` | HTTP firmware check; returns the expected 404. |
|
| `PORT_FIRMWARE` | `8005` | HTTP firmware check; returns the expected 404. |
|
||||||
| `PORT_XMPP` | `5223` | Plaintext robot XMPP listener; advertised for `EcoMsgNew`. |
|
| `PORT_XMPP` | `5223` | Plaintext robot XMPP listener; advertised for `EcoMsgNew`. |
|
||||||
| `HEALTH_PORT` | `8080` | HTTP `GET /healthz` and the `healthcheck` subcommand. |
|
| `HEALTH_PORT` | `8080` | HTTP `GET /healthz` and the `healthcheck` subcommand. Bind failure is non-fatal; robot listeners keep running. |
|
||||||
| `MQTT_HOST` | required | Broker DNS name or address, without a scheme or port. Also used as the TLS server name. |
|
| `MQTT_HOST` | required | Broker DNS name or address, without a scheme or port. Also used as the TLS server name. |
|
||||||
| `MQTT_PORT` | `1883` or `8883` | Broker port; defaults to 8883 when `MQTT_TLS=true`, otherwise 1883. |
|
| `MQTT_PORT` | `1883` or `8883` | Broker port; defaults to 8883 when `MQTT_TLS=true`, otherwise 1883. |
|
||||||
| `MQTT_TLS` | `false` | Enable TLS from the start of the MQTT TCP connection. Uses the system CA pool and verifies the broker hostname. |
|
| `MQTT_TLS` | `false` | Enable TLS from the start of the MQTT TCP connection. Uses the system CA pool and verifies the broker hostname. |
|
||||||
|
|||||||
+15
-3
@@ -99,15 +99,26 @@ func run(ctx context.Context, environ []string) error {
|
|||||||
shutdownXMPP = xmppServer.Shutdown
|
shutdownXMPP = xmppServer.Shutdown
|
||||||
}
|
}
|
||||||
|
|
||||||
errCh := make(chan error, 4)
|
// Robot-facing listeners. A failure here stops the process.
|
||||||
|
const robotListeners = 3
|
||||||
|
errCh := make(chan error, robotListeners)
|
||||||
go func() { errCh <- httpx.ServeLookup(groupCtx, cfg) }()
|
go func() { errCh <- httpx.ServeLookup(groupCtx, cfg) }()
|
||||||
go func() { errCh <- httpx.ServeFirmware(groupCtx, cfg) }()
|
go func() { errCh <- httpx.ServeFirmware(groupCtx, cfg) }()
|
||||||
go func() { errCh <- httpx.ServeHealth(groupCtx, cfg) }()
|
|
||||||
go func() { errCh <- xmppServer.Serve(groupCtx) }()
|
go func() { errCh <- xmppServer.Serve(groupCtx) }()
|
||||||
|
|
||||||
|
// Health is optional. Occupied 8080 (OTBR and other HA add-ons) must
|
||||||
|
// not tear down lookup/XMPP.
|
||||||
|
go func() {
|
||||||
|
if err := httpx.ServeHealth(groupCtx, cfg); err != nil && groupCtx.Err() == nil {
|
||||||
|
slog.Error("health listener failed; robot listeners continue", "err", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var runErr error
|
var runErr error
|
||||||
|
received := 0
|
||||||
select {
|
select {
|
||||||
case runErr = <-errCh:
|
case runErr = <-errCh:
|
||||||
|
received++
|
||||||
cancel()
|
cancel()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
}
|
}
|
||||||
@@ -122,10 +133,11 @@ func run(ctx context.Context, environ []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
for i := 0; i < 4; i++ {
|
for received < robotListeners {
|
||||||
if err := <-errCh; err != nil && runErr == nil {
|
if err := <-errCh; err != nil && runErr == nil {
|
||||||
runErr = err
|
runErr = err
|
||||||
}
|
}
|
||||||
|
received++
|
||||||
}
|
}
|
||||||
|
|
||||||
if shutdownCtx.Err() != nil {
|
if shutdownCtx.Err() != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package httpx
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@@ -21,6 +22,7 @@ func ServeFirmware(ctx context.Context, cfg config.Config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", addr, err)
|
return fmt.Errorf("%s: %w", addr, err)
|
||||||
}
|
}
|
||||||
|
slog.Info("firmware listening", "addr", addr)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package httpx
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,6 +24,7 @@ func ServeHealth(ctx context.Context, cfg config.Config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", addr, err)
|
return fmt.Errorf("%s: %w", addr, err)
|
||||||
}
|
}
|
||||||
|
slog.Info("health listening", "addr", addr)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func ServeLookup(ctx context.Context, cfg config.Config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s: %w", addr, err)
|
return fmt.Errorf("%s: %w", addr, err)
|
||||||
}
|
}
|
||||||
|
slog.Info("lookup listening", "addr", addr)
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ func (s *Server) Serve(ctx context.Context) error {
|
|||||||
return fmt.Errorf("%s: %w", addr, err)
|
return fmt.Errorf("%s: %w", addr, err)
|
||||||
}
|
}
|
||||||
s.ln = ln
|
s.ln = ln
|
||||||
|
slog.Info("xmpp listening", "addr", addr)
|
||||||
|
|
||||||
errCh := make(chan error, 1)
|
errCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
Reference in New Issue
Block a user