941abd98d5
Dynamic MySolem/MyIndygo IoT bridge with MQTT, Home Assistant autodiscovery, and Homebridge EasyMQTT compatibility. - Device discovery via MySolem API on startup (all modules/inputs/outputs) - Configurable poll engine (fast/normal/slow buckets, per-input overrides) - MQTT retained state publishing + HA autodiscovery payloads - Unified manual command routing (linesControl vs sendManualModuleCommand) - Runtime config via MQTT $config/set, persisted to config.json - Expression override system (API-provided > hardcoded > passthrough) - Minimal REST API: /health, /state, /control, /rediscover - Docker deployment (Dockerfile + docker-compose.yml) - 38 unit tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
201 lines
5.8 KiB
Markdown
201 lines
5.8 KiB
Markdown
# myprimal
|
||
|
||
MQTT bridge for the MySolem / MyIndygo IoT platform (irrigation + pool). Reverse-engineered client that dynamically discovers all devices and exposes them via MQTT with Home Assistant autodiscovery and Homebridge EasyMQTT compatibility.
|
||
|
||
## Features
|
||
|
||
- Auto-discovers all modules, sensors, and outputs on startup
|
||
- Polls sensor readings on configurable intervals
|
||
- Publishes to MQTT with retained state
|
||
- Home Assistant MQTT autodiscovery (sensors + switches)
|
||
- Compatible with Homebridge [EasyMQTT](https://github.com/Shaquu/homebridge-easymqtt) plugin
|
||
- Manual commands via MQTT (`/set` topics)
|
||
- Runtime poll interval tuning via MQTT
|
||
- REST API for health, state snapshot, and direct control
|
||
- Docker deployment
|
||
|
||
## Setup
|
||
|
||
### 1. Configure
|
||
|
||
Copy `.env.example` to `.env` and fill in credentials:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
Required:
|
||
- `SOLEM_EMAIL` / `SOLEM_PASSWORD` — MySolem account credentials
|
||
- `MQTT_URL` — your MQTT broker (e.g. `mqtt://192.168.1.10:1883`)
|
||
|
||
### 2. Run locally
|
||
|
||
```bash
|
||
npm install
|
||
node src/index.js
|
||
```
|
||
|
||
### 3. Run with Docker
|
||
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
On first run, `config.json` is created in the Docker volume for persistent poll interval settings.
|
||
|
||
## MQTT Topics
|
||
|
||
All topics are prefixed with `MQTT_TOPIC_PREFIX` (default: `myprimal`).
|
||
|
||
### Sensor readings (retained)
|
||
|
||
```
|
||
myprimal/{module_slug}/sensor/{metric}
|
||
```
|
||
|
||
Payload: `{"value": 24.5, "unit": "°C", "ts": "2026-06-28T10:00:00.000Z"}`
|
||
|
||
Examples:
|
||
```
|
||
myprimal/lrpc_f1da27/sensor/temperature {"value": 24.5, "unit": "°C", ...}
|
||
myprimal/lrps_0565c0/sensor/ph {"value": 7.35, "unit": "pH", ...}
|
||
myprimal/lrps_0565c0/sensor/orp_redox {"value": 690, "unit": "mV", ...}
|
||
myprimal/lr6ag_070917/sensor/plumbago {"value": 54, "unit": "%", ...}
|
||
```
|
||
|
||
### Output state (retained)
|
||
|
||
```
|
||
myprimal/{module_slug}/output/{index}/state
|
||
```
|
||
|
||
Payload: `{"value": 0, "ts": "..."}` — `value` 1 = running, 0 = stopped
|
||
|
||
### Commands
|
||
|
||
```
|
||
myprimal/{module_slug}/output/{index}/set
|
||
```
|
||
|
||
Payload:
|
||
```json
|
||
{ "action": "run", "duration": 30 } // run for 30 minutes
|
||
{ "action": "stop" } // stop immediately
|
||
{ "action": "boost", "duration": 120 } // boost (pool pump only)
|
||
{ "action": "pause", "days": 3 } // pause/rain delay N days
|
||
```
|
||
|
||
### Availability (retained)
|
||
|
||
```
|
||
myprimal/{module_slug}/availability "online" | "offline"
|
||
```
|
||
|
||
### Config (retained, runtime-tunable)
|
||
|
||
Read current config:
|
||
```
|
||
myprimal/$config
|
||
```
|
||
|
||
Update poll intervals (seconds):
|
||
```
|
||
myprimal/$config/set {"poll": {"fast": 30, "normal": 120}}
|
||
```
|
||
|
||
Update interval for a specific sensor:
|
||
```
|
||
myprimal/$config/set {"poll": {"overrides": {"lrps_0565c0/ph": 30}}}
|
||
```
|
||
|
||
Changes take effect immediately and persist across restarts.
|
||
|
||
### Poll buckets (defaults)
|
||
|
||
| Bucket | Default | Sensor types |
|
||
|---|---|---|
|
||
| `fast` | 60s | pH, ORP, pressure, water temperature |
|
||
| `normal` | 300s | Soil moisture, air temperature, flow |
|
||
| `slow` | 900s | Binary sensors (rain, level switch, valve state) |
|
||
|
||
## Home Assistant
|
||
|
||
Entities are auto-created via MQTT discovery on startup. Check **Settings → Devices & Services → MQTT** — all Solem devices appear automatically.
|
||
|
||
Discovery topics: `homeassistant/{sensor|binary_sensor|switch}/myprimal_{id}/config`
|
||
|
||
No manual configuration required.
|
||
|
||
## Homebridge (EasyMQTT)
|
||
|
||
Install [homebridge-easymqtt](https://github.com/Shaquu/homebridge-easymqtt) and configure accessories pointing to the MQTT topics above.
|
||
|
||
Example for pool light switch:
|
||
```json
|
||
{
|
||
"accessory": "EasyMQTT",
|
||
"name": "Pool Light",
|
||
"type": "Switch",
|
||
"mqttGetTopic": "myprimal/lrpc_f1da27/output/1/state",
|
||
"mqttSetTopic": "myprimal/lrpc_f1da27/output/1/set",
|
||
"mqttGetTransformation": "return JSON.parse(message).value === 1 ? 'true' : 'false';",
|
||
"mqttSetTransformation": "return value === 'true' ? JSON.stringify({action:'run',duration:30}) : JSON.stringify({action:'stop'});"
|
||
}
|
||
```
|
||
|
||
## REST API
|
||
|
||
| Method | Path | Description |
|
||
|---|---|---|
|
||
| `GET` | `/health` | Service health + counts |
|
||
| `GET` | `/state` | All cached sensor readings |
|
||
| `GET` | `/state/:moduleId` | Readings for one module |
|
||
| `GET` | `/registry` | Full device registry |
|
||
| `POST` | `/control/:moduleId/:outputIndex` | Send manual command |
|
||
| `POST` | `/rediscover` | Re-discover all devices |
|
||
|
||
### Control example
|
||
|
||
```bash
|
||
# Run pool light (LRPC output 1) for 30 minutes
|
||
curl -X POST http://localhost:3001/control/6481bcea8782b78554c02bed/1 \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"action":"run","duration":30}'
|
||
|
||
# Stop
|
||
curl -X POST http://localhost:3001/control/6481bcea8782b78554c02bed/1 \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"action":"stop"}'
|
||
```
|
||
|
||
## Module Slug Reference (Domicile)
|
||
|
||
| Module | Slug | Type |
|
||
|---|---|---|
|
||
| LRPC-F1DA27 (pool controller) | `lrpc_f1da27` | Filtration pump (output 0), light (output 1) |
|
||
| LR6AG-070917 (irrigation) | `lr6ag_070917` | 6 irrigation stations (outputs 0–5) |
|
||
| LRMS-0721DC (sensor station) | `lrms_0721dc` | Soil moisture × 2, air temp |
|
||
| LRPS-0565C0 (pool analyser) | `lr_mas_0565c0` | Water temp, pH, ORP |
|
||
| LRPR-0D6800 (filter pressure) | `lr_pr_0d6800` | Filter pressure |
|
||
| LRLEVEL-0D2ED9 (fill valve) | `lr_niv_0d2ed9` | Fill valve control |
|
||
| LR2IPECO-0C9282 | `lr_ip_eco_0c9282` | Flow meter |
|
||
| POOL-LVL-0E953B (level switch) | `pool_level_sensor_0e953b` | Pool level binary |
|
||
|
||
## Architecture
|
||
|
||
```
|
||
MySolem API ──► solem.js (auth + HTTP)
|
||
│
|
||
registry.js (device discovery on startup)
|
||
│
|
||
poller.js (per-bucket poll timers)
|
||
│
|
||
state.js (in-memory cache, EventEmitter)
|
||
│
|
||
┌─────────┴──────────┐
|
||
mqtt/publish.js api/routes.js
|
||
mqtt/discovery.js (REST API)
|
||
mqtt/commands.js ──► control.js ──► MySolem API
|
||
mqtt/config-bridge.js
|
||
```
|