Add lights/jets/salt control, app-traffic mimicry, and optional local read path

- New entities: number (salt output level 0-10), switch (lights, jets); the
  read-only salt sensor is replaced by the settable number.
- Look identical to the official app on the wire: send the app's Dalvik UA on
  REST, okhttp/4.12.0 on the mTLS creds fetch, and "mqtt-<uuid>" client ids.
- Consolidate every constant value into constants.py (well commented); protocol.py
  keeps only the command builders; const.py removed.
- Optional local read path: a `local_status_url` config option polls the dongle's
  local /status endpoint for telemetry instead of the cloud MQTT subscription
  (control stays cloud MQTT). State getters handle both schemas.

No private/internal values in the repo (the spa IP is runtime config only).

Fixes bug-41mqxddz7zeh

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F6B8b3iYNv6QUftK2FDfYb
This commit is contained in:
Hank Mueller
2026-09-01 15:12:24 +00:00
co-authored by Claude Opus 5
parent 8cda06f7f3
commit cde2bbc772
16 changed files with 497 additions and 151 deletions
+18 -4
View File
@@ -3,9 +3,15 @@
Monitor and control a **Watkins / Hot Spring "Connected Spa"** hot tub from Home
Assistant, without the vendor mobile app.
- **Climate** entity — target setpoint + current water temperature (heater).
- **Sensors** — water temperature, salt output level, salt cartridge state.
- Cloud-push: live telemetry over the vendor's HiveMQ Cloud MQTT broker.
- **Climate** — target setpoint + current water temperature (heater).
- **Number** — FreshWater salt output level (010), settable.
- **Switches** — spa lights (all zones) and jets.
- **Sensors** — water temperature, salt cartridge state.
- Live telemetry over the vendor's HiveMQ Cloud MQTT broker, or — optionally —
polled from the spa's local `/status` endpoint (see below).
Requests are sent with the same User-Agents and MQTT client-id format the
official app uses, so the traffic is indistinguishable from it.
This is an **unofficial** integration reverse-engineered from the official app.
It talks to Watkins' cloud; there is **no local control path**. If Watkins
@@ -63,7 +69,15 @@ openssl x509 -inform der -in ca_cert.der -out ca_cert.pem
for commands (temperature is an integer string in the spa's unit).
The reusable protocol client lives under `custom_components/hotspring/api/`
(`cloud.py`, `spa.py`, `protocol.py`) and has no Home Assistant dependency.
(`cloud.py`, `spa.py`, `protocol.py`); all constant values are in `constants.py`.
## Optional: local read path
If Home Assistant is on the spa's LAN, set **Local `/status` URL** in the config
flow (e.g. `http://<spa-ip>/status`). Telemetry is then polled from the dongle's
local HTTP endpoint instead of subscribed over the cloud broker, keeping
monitoring on-LAN and off the vendor cloud. Control still uses the cloud MQTT
connection. Leave it blank to use cloud MQTT for reads.
## License