arnaudne d6cde25504 fix: use serial positions 6-11 for module slug instead of last 6 chars
Serial format is PPPPPP+SSSSSS+0001 — slice(-6) was picking up the
trailing 0001 suffix. slice(6,12) gives the correct device identifier.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 01:29:24 +02:00

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 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:

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

npm install
node src/index.js

3. Run with Docker

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:

{ "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 and configure accessories pointing to the MQTT topics above.

Example for pool light switch:

{
  "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

# 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 05)
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
S
Description
No description provided
Readme 172 KiB
Languages
JavaScript 99.7%
Dockerfile 0.3%