Files
myprimal/api-docs/myindygo.md
T
arnaudne 941abd98d5 feat: initial myprimal MQTT bridge service
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>
2026-06-28 01:05:24 +02:00

368 lines
12 KiB
Markdown
Raw 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.
# MyIndygo — Platform Overview & API Reference
**Base URL**: `https://qualif.myindygo.com`
**Purpose**: Pool-focused platform — filtration scheduling, water chemistry, pool level monitoring
**Relation to MySOLEM**: Same backend database and Sails.js server. Both platforms share all module data, sensor readings, and programs. MyIndygo adds a pool admin/management layer on top.
---
## Authentication
Identical to MySOLEM — same cookie name, same credentials work on both platforms.
```bash
curl -c cookies.txt https://qualif.myindygo.com/login
curl -c cookies.txt -b cookies.txt \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "email=YOUR_EMAIL&password=YOUR_PASSWORD" \
"https://qualif.myindygo.com/login"
```
Cookie: `qualification-mysolem-solem-irrigation-platform.sid` (same name as MySOLEM)
---
## User IDs — MyIndygo vs MySOLEM
The test API account (`arnaud.nelissen.api@icloud.com`) has **different ObjectIds** on each platform:
| Platform | User ID |
|---|---|
| MySOLEM | `5de53bca7ed4c45c2cfe067f` |
| MyIndygo | `6a21c3db5ebe0fa5f7885460` |
The MyIndygo user ID appears in the page HTML:
```html
<script>const userId = "6a21c3db5ebe0fa5f7885460"</script>
```
The pool/site ID `5de53c1c7ed4c45c2cfe0688` is the main account's pool record on MyIndygo — it's only accessible when logged in as the main account (`anelissen@solem.fr`), not the test API account.
---
## Feature flags (MyIndygo-specific)
`GET /features` returns pool-focused flags including:
| Flag | Value | Meaning |
|---|---|---|
| `ocedisWaterChemistryCorrection` | `true` | Water chemistry correction (OCEDIS dosing) |
| `authenticationMyIndygoUIV2` | `true` | MyIndygo-specific login UI |
| `enableBSTSubscriptions` | `true` | BST subscription management |
| `enablePWAIndygoInstallation` | `true` | Pool app PWA install |
| `clusterTurfOpened` | `true` | Turf cluster watering |
| `enablePolytropicAPI` | `true` | Polytropic pump integration |
| `enableGraphicalProgrammation` | `true` | Visual program editor |
| `enableNdviSatellite` | `true` | NDVI satellite data |
| `enableEvapotranspiration` | `true` | ET-based watering |
| `enableHPA` | `true` | HPA (high-pressure assist?) |
---
## Pool Admin System (B2B)
MyIndygo has a pool dealer admin layer for managing multiple customer pools. These endpoints are **admin-only** — not accessible with a standard user account.
```
GET /pools/admin list all managed pools
POST /pools/admin/search search pools (DataTables format)
POST /pools/admin/sendMessage send a message to a pool customer
GET /pools/admin/getMessages get admin messages
DELETE /pools/admin/dissociateAndRemove/{id} remove pool from admin
```
Pool data is fetched via:
```
GET /user/pools current user's pools (returns {"pools": []} for test account)
GET /user/pools?userId= another user's pools (IDOR — no ownership check)
```
---
## Pool-Specific Endpoints
### Last sensor tick (no date range needed)
```
GET /input/getLastTickFromInput?inputId={inputId}
```
Returns the single most-recent raw tick for an input. Much lighter than `getComputedSensorData`.
Response:
```json
{
"tick": {
"input": "6480da07c2c0896da120eccd",
"value": 635,
"timestamp": "2026-06-04T01:03:00.000Z",
"createdAt": "2026-06-04T01:03:11.000Z",
"id": "..."
}
}
```
Note: `value` is **raw** (not expression-computed). For LRPS pH: `635` = 6.35 pH (divide by 100). For LRPS temp: `2331` = 23.31°C.
### Live pool sensor snapshot
```python
import requests
BASE = "https://qualif.myindygo.com"
s = requests.Session()
s.get(f"{BASE}/login")
s.post(f"{BASE}/login", data={"email": "...", "password": "..."})
POOL_INPUTS = {
"temp": "6480da07c2c0896da120eccc",
"pH": "6480da07c2c0896da120eccd",
"ORP": "6480da07c2c0896da120ecce",
"pressure": "6a17218be0b88ea3e9255251",
"level": "6a174dd1e0b88ea3e925531e",
}
readings = {}
for name, input_id in POOL_INPUTS.items():
r = s.get(f"{BASE}/input/getLastTickFromInput", params={"inputId": input_id}).json()
readings[name] = r.get("tick", {})
# Current values (raw):
# temp → divide by 100 → °C
# pH → divide by 100 → pH units
# ORP → raw mV
# pressure → raw ADC (apply expression for bar)
# level → 0=low/fill needed, 1=OK
```
### Output events history
```
GET /output/events?moduleId={moduleId}
GET /output/events?outputId={outputId}
GET /output/events?moduleId={id}&startDate={iso8601}&endDate={iso8601}
```
Returns `[]` if no events or empty. Tracks when outputs have been triggered.
### Output watering forecast
```
GET /output/forecast?moduleId={moduleId}
GET /output/forecast?outputId={outputId}
```
Returns upcoming scheduled watering windows.
### Real-time module informations (via LoRa)
```
GET /remote/module/informations
?moduleId={relayModuleId}
&childrenSerialNumber={childSerialNumber}
```
Or via socket.io (as seen in JS):
```js
io.socket.request({
method: "get",
url: "/remote/module/informations",
data: {
moduleId: relayId, // LRMB10 gateway ID
moduleUuid: uuid,
moduleSerialNumber: serialNumber
}
}, callback)
```
Returns real-time device state polled over LoRa. Fails with `module_not_reachable` if device is offline.
### Real-time combined config + programs
```
GET /remote/module/configuration/and/programs?moduleId={moduleId}
```
Returns configuration + programs in a single LoRa roundtrip. Requires device online.
### Pool controller control (linesControl)
JSON body (verified from real network capture):
```
POST /remote/module/control
Content-Type: application/json
```
```json
{
"moduleSerialNumber": "150302F1DA270001",
"linesControl": [
{
"index": 1,
"action": 2,
"time": "30:00"
}
]
}
```
Known `action` codes for LRPC pool controller:
| action | meaning | extra params |
|---|---|---|
| `0` | Stop pump output (index 0 only) | — |
| `1` | **Stop / cancel** any running manual command | — |
| `2` | Timed impulse | `time`: `"MM:SS"` string (e.g. `"30:00"` = 30 minutes) |
| `3` | Boost filtration | `time`: `"MM:SS"` |
| `4` | Start indefinitely | — |
| `5` | Pause N days (blocks manual + scheduled) | `manualDuration`: N (integer days) |
| `11` | Backwash cycle | `time`: encoded duration string, `speed`: pump speed level |
Response pool state includes `"pause": N` (days remaining) when an output is paused.
Response always reflects **pre-command state** — the command takes effect on the next LoRa downlink.
For backwash, duration is encoded using `encodeBackwashDuration(durationMinutes, minMinutes, maxMinutes)`.
> **Corrected from JS analysis**: duration is `time: "MM:SS"` (string), NOT `manualDuration` in seconds. Verified from real Safari capture.
### Module advertising profile
```
GET /remote/module/advertisingProfile?moduleId={moduleId}
GET /remote/modules/advertisingProfile (bulk)
```
BLE advertising profile data. Likely used for Bluetooth pairing.
### Team pool access
```
GET /teams/pools/{poolId}/membersAccessGranted
```
Returns `{ teamMembersAlreadyGrantedPool: [...] }` — team members with access to a specific pool.
---
## LRPC Pool Controller — Program Structure (MyIndygo extended)
MyIndygo exposes more program fields than MySOLEM for `lr-pc` modules:
```
GET /programs/modules?moduleIds={moduleId}
```
```json
{
"programs": [
{
"id": "6481bcea8782b78554c02bf4",
"name": "",
"module": "6481bcea8782b78554c02bed",
"index": 0,
"weekDays": 127,
"numberOfDays": 2,
"synchroDay": 0,
"waterBudget": 100,
"startTimes": [-1, -1, -1, -1, -1, -1, -1, -1],
"programCharacteristics": {
"programType": 4,
"mode": 2,
"onSpeed": 1,
"speedSequence": 4368,
"microFilteringTemperature": 5,
"microFilteringDuration": 10,
"microFilteringPeriod": 480,
"microFilteringSpeed": 1,
"auxiliaryCyclicDuration": 0,
"frostFreeSpeed": 1,
"defaultProgramSpeed": 1,
"adaptOffset": 0,
"isShutterDependant": false,
"isRandom": false,
"isUsingButton": false
},
"windows": [
{
"start": 300, "end": 360,
"on": 3600, "off": 0,
"runningDays": 127
}
]
}
]
}
```
**`programType` codes** (pool controller):
| code | meaning |
|---|---|
| `4` | Main filtration (pump runs on schedule) |
| `2` | Auxiliary / cyclic (treatment dosing, UV, etc.) |
| `0` | Disabled / empty slot |
**`mode`**: `2` = active, `0` = disabled
**`speedSequence`**: pump speed bitmask for variable-speed pumps (`4368` = 0x1110)
**`windows[].runningDays`**: per-window day bitmask (127 = every day, unlike mysolem where weekDays is per-program)
**`startTimes`**: array of 8 fixed start times in minutes from midnight (-1 = unused)
### Domicile LRPC filtration schedule
| Program | Type | Windows | Total |
|---|---|---|---|
| 0 (Filtration) | 4 | 05:0006:00 (1h), 12:0014:00 (2h), 22:0023:00 (1h) | 4h/day |
| 1 (Auxiliary) | 2 | 21:3022:30 (1h) | 1h/day |
| 2 | — | empty | — |
---
## LRPC Outputs (Pool Controller)
| Output ID | Name | Index |
|---|---|---|
| `6481bcea8782b78554c02bf1` | Station 1 | 0 |
| `6481bcea8782b78554c02bf2` | Station 2 | 1 |
| `6481bcea8782b78554c02bf3` | Station 3 | 2 |
---
## New Module Types (found via modules/search on MyIndygo)
| Type | Name | Description |
|---|---|---|
| `lr-pc-vs` | LRPCVS | Pool controller with variable speed pump |
| `lr-mv` | LRMV/LRPM1 | Motor valve (multi-output) |
| `lr-mb-30` | LRMB30 | LoRa gateway v30 |
| `lr-mb-100` | LRMB100 | LoRa gateway v100 |
| `lr-ip-fl` | LR1IPFL | IP flow metering |
| `lr-ip-fl-v2` | LR6IP | IP flow v2 |
| `lr-ip-wan-v2` | LRIP6 WAN | IP WAN v2 |
| `lr-is` | LR6IS/LR12IS | LoRa input sensor (multi-input) |
| `lr-is-city` | LR6IS | City-variant input sensor |
| `lr-is-city-wan` | LR12IS | City WAN sensor |
| `lr-ms-eco` | LRMS_ECO | Eco sensor station |
| `lr-ms-rs485` | LRMSRS485 | RS485 sensor station |
| `lr-ms-v2` | LRMS v2 | Sensor station v2 |
| `lr-ol` | LROL4 | LoRa OL (lighting?) |
| `lr-bst-100` | LRBST100 | BST 100 (booster?) |
| `lr-bst-react-4g` | LR-BST R4G | BST 4G reactive (booster 4G) |
| `joro` / `joro-v2` | WB-xxxx | Joro water balance meter |
| `smart-is` | SmartIS | Smart input sensor |
| `smart-is-lcd` | VILLA-/SMT-HOME | Smart IS with LCD display |
| `bl-ip-v2` | BL6IP | Bluetooth IP v2 |
| `ipx` | GEN_xxxx | IPX controller (generic) |
---
## Shared Endpoints (same on both MySOLEM and MyIndygo)
All endpoints from `api-docs/endpoints.md` work identically on `https://qualif.myindygo.com`. The backend database is shared — module IDs, input IDs, programs, and sensor data are identical on both platforms.
Key shared endpoints that work well for pool monitoring:
- `GET /remote/input/getComputedSensorData?inputId=...` — time-series sensor data
- `GET /remote/module/getComputedSensorsData?moduleId=...` — all inputs for a module
- `POST /module/sendManualModuleCommand` — send commands to devices
- `GET /modules/{moduleId}/notebook?startDate={unix_epoch}` — event log
- `GET /programs/modules?moduleIds=...` — read/write programs
---
## Pool Monitoring Quick Reference
Current pool sensor IDs and live values (2026-06-04):
| Sensor | Input ID | Last Raw Value | Computed |
|---|---|---|---|
| Water temp (LRPS) | `6480da07c2c0896da120eccc` | 2331 | 23.31 °C |
| pH (LRPS) | `6480da07c2c0896da120eccd` | 635 | 6.35 (low — target 7.27.6) |
| ORP/Redox (LRPS) | `6480da07c2c0896da120ecce` | 646 | 646 mV |
| Filter pressure (LRPR) | `6a17218be0b88ea3e9255251` | 956 | — |
| Pool level switch | `6a174dd1e0b88ea3e925531e` | 1 | OK (1=full) |
| Fill flow volume | `6a1c4b153d6c33ab843edc15` | 0 | 0 L (no active fill) |
| Fill valve state | `6a1c4b153d6c33ab843edc16` | 1 | Closed |