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>
12 KiB
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.
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:
<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:
{
"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
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):
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
{
"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), NOTmanualDurationin 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}
{
"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:00–06:00 (1h), 12:00–14:00 (2h), 22:00–23:00 (1h) | 4h/day |
| 1 (Auxiliary) | 2 | 21:30–22: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 dataGET /remote/module/getComputedSensorsData?moduleId=...— all inputs for a modulePOST /module/sendManualModuleCommand— send commands to devicesGET /modules/{moduleId}/notebook?startDate={unix_epoch}— event logGET /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.2–7.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 |