Files
Hank MuellerandClaude Opus 5 cde2bbc772 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
2026-09-01 15:12:24 +00:00

85 lines
3.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hot Spring Connected Spa — Home Assistant integration
Monitor and control a **Watkins / Hot Spring "Connected Spa"** hot tub from Home
Assistant, without the vendor mobile app.
- **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
changes their API it may break.
## Security / credentials
This repository contains **no secrets**:
- Your **account e-mail + password** are entered in the config-flow UI when you
add the integration and stored in Home Assistant's encrypted config entry —
never in this repo.
- The vendor **mutual-TLS client certificate** required to fetch broker
credentials is Watkins material (extractable from the app APK). It is **not
redistributed here**. You provide it as three PEM files on the host (below).
## Prerequisites: the client certificate
The integration needs three files in a directory on the Home Assistant host
(default `/config/hotspring/certs/`):
| File | Origin |
|---|---|
| `client1_cert.pem` | client cert from the app APK (`res/raw/client1.p12`) |
| `client1_key.pem` | its private key |
| `ca_cert.pem` | firmware CA (`res/raw/ca_cert.der`) |
To produce them from the APK's `client1.p12` and `ca_cert.der` (the P12 is
protected by a short static password embedded in the app — recover it from the
decompiled APK and pass it to `openssl` below):
```sh
P12PASS=... # static password embedded in the app APK
openssl pkcs12 -legacy -in client1.p12 -passin pass:"$P12PASS" -clcerts -nokeys -out client1_cert.pem
openssl pkcs12 -legacy -in client1.p12 -passin pass:"$P12PASS" -nocerts -nodes -out client1_key.pem
openssl x509 -inform der -in ca_cert.der -out ca_cert.pem
```
## Install
1. Copy `custom_components/hotspring/` into your Home Assistant `config/custom_components/`.
2. Place the three PEM files in `/config/hotspring/certs/`.
3. Restart Home Assistant.
4. **Settings → Devices & Services → Add Integration → “Hot Spring Connected
Spa”**, and sign in with your account.
## How it works
1. `POST iotsupportportalapi.watkinsmfg.com/api/v1/login/` → JWT.
2. `GET /user_spa_details/` → the spa's `rootTopic` (MQTT prefix).
3. mTLS `GET iotfirmwareota.watkinsmfg.com:8577/` (client cert) → rotating
HiveMQ broker host/user/pass.
4. Subscribe `<rootTopic>/#` for telemetry; publish
`<rootTopic>/<subsystem>/control` with `{"<subsystem>":{"control":{…}}}`
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`); 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
MIT — see [LICENSE](LICENSE).