Files
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

12 KiB
Raw Permalink Blame History

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), 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}
{
  "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