Elco / Ariston Cloud Integration¶
The home runs an ELCO Thision S 14.2 gas boiler with a Remocon-NET cloud gateway. Home Assistant integrates with it through an AppDaemon bridge (elco-remocon-net-appdaemon at https://gitlab.mdapi.ch/mdapi/elco-remocon-net-appdaemon, a fork of nechry/elco-remocon-net-appdaemon) that polls the Ariston Group cloud and writes setpoints, modes and holiday state back through it.
Architecture¶
flowchart LR
boiler["Thision S 14.2\nBSB controller"]
gw["Remocon-NET\ngateway F0AD…"]
cloud["Remocon-NET cloud\nAWS Elastic Beanstalk\neu-west-1"]
ad["AppDaemon\nelco-remocon-net-appdaemon"]
ha["Home Assistant\nsensors, helpers,\ndashboards, automations"]
boiler <--BSB--> gw
gw <--MQTT--> cloud
ad <--HTTPS--> cloud
ad --REST--> ha
ha --service calls--> ad
The bridge has no local connection to the boiler — every read and write round-trips through the Ariston cloud. Latency is ~5 s for a write to be visible at the boiler, ~30–60 s for full state convergence.
Cloud API surfaces¶
The Remocon-NET cloud at www.remocon-net.remotethermo.com exposes two distinct API surfaces on the same backend (both resolve to the same AWS ELB):
| Surface | Used by | Auth | Style |
|---|---|---|---|
Legacy /R2/... |
Web UI (https://www.remocon-net.remotethermo.com) |
.AspNet.ApplicationCookie from POST /R2/Account/Login |
ASP.NET MVC, viewmodel-style envelopes |
Modern /api/v2/remote/... |
Remocon-NET mobile app (Android, ktor-client) | Same session cookie or ar.authToken header |
Clean REST, one resource per endpoint |
Both APIs accept the same session cookie
The ar.authToken header used by the mobile app is an alternate auth scheme that's not required. A standard cookie session from /R2/Account/Login authenticates against both surfaces. The AppDaemon bridge uses only the cookie path.
Endpoints in use¶
Reads come from the legacy surface (it returns everything the bridge needs in one round-trip):
| Method | Path | Purpose |
|---|---|---|
POST |
/R2/Account/Login?returnUrl=HTTP/2 |
login → session cookie |
POST |
/R2/PlantHomeBsb/GetData/<gw> |
plant + zone + (optionally) schedule |
Writes use the modern surface where it works better than the legacy one:
| Method | Path | Purpose |
|---|---|---|
POST |
/R2/PlantHomeBsb/SetData/<gw> |
mode / DHW comfort+reduced / DHW mode |
POST |
/R2/PlantTimeProgBsb/SetTemperature/<gw> |
CH schedule-slot temperatures |
POST |
/R2/PlantTimeProgBsb/SetTimeProg/<gw> |
full weekly schedule rewrite |
POST |
/api/v2/remote/bsbHolidays/<gw>/<zone> |
holiday slot writes |
POST |
/R2/PlantMenuBsb/WriteDataPoints/<gw> |
direct BSB datapoint writes (cache repair) |
Legacy /R2/PlantHomeBsb/SetData cannot ADD holidays
A holiday write through SetData returns ok:true and the cloud silently drops it. The web UI suffers from the same bug — when you save a holiday in the browser the change is visible until you reload, then it vanishes. Holiday writes only work through the modern /api/v2/remote/bsbHolidays/<gw>/<zone> endpoint.
Holiday model¶
The BSB controller exposes a fixed array of 8 holiday slots (indices 0–7). Each slot has a from and to date. Active criterion is from ≤ today ≤ to; unused slots are filled with the empty marker 2026-01-01T00:00 → 2026-01-01T00:00 (a zero-length range that the boiler ignores).
POST /api/v2/remote/bsbHolidays/<gw>/<zone> body:
{
"currentZoneMode": 1,
"new": {"index": 0, "from": "YYYY-MM-DDT00:00", "to": "YYYY-MM-DDT00:00"},
"old": {"index": 0, "from": "...", "to": "...", "osv": true}
}
new.indexselects which slot to write (0–7). The bridge always writes slot 0 to mirror the mobile app.oldis the slot's prior state for optimistic concurrency. The bridge GETs the slot first and uses what the cloud returned.- ADD / MODIFY / DELETE are all the same operation — pick a slot, set the new (from, to). Clearing a slot writes the empty marker.
Year is annotative
The boiler firmware stores only the month and day for each holiday endpoint. The cloud annotates with the current year on read. Sending 2030-07-19 gets stored as 2026-07-19 (current year). For HA, always send dates in the current year; the calendar event triggers will handle the year naturally.
BSB datapoint addresses¶
The cloud exposes a generic POST /R2/PlantMenuBsb/WriteDataPoints/<gw> endpoint that writes BSB controller parameters by 7-digit address. Two addresses are useful operationally:
| Address | Parameter | Range |
|---|---|---|
2950542 |
CH zone 1 comfort setpoint | 10–35 °C, step 0.5 |
2950544 |
CH zone 1 reduced setpoint | 10–19.5 °C, step 0.5 |
Request shape:
POST /R2/PlantMenuBsb/WriteDataPoints/<gw>
Content-Type: application/json
[
{"address": 2950542, "newValueAsNumber": 19.5, "oldValueAsNumber": 4.0,
"newValueAsString": null, "oldValueAsString": null,
"newOsv": false, "oldOsv": false}
]
Cache repair via WriteDataPoints
The legacy PlantHomeBsb/SetData endpoint can leave the cloud's cached view of CH setpoints in an inverted-sentinel state (value=4.0, min=10, max=4) if a write fails. The boiler itself stays fine; only the cloud cache goes bad. Writing the correct values via WriteDataPoints clears it. Equivalent workaround via UI: open Remocon-NET mobile → Chauffage → Scheduling → re-apply the comfort and reduced setpoints.
Reading patterns¶
POST /R2/PlantHomeBsb/GetData/<gw> with a non-null filter.progIds returns the heating zone schedule alongside plant + zone data, saving a second round-trip:
progIds enum (from the JS bundle): 1..6 = ChZn1..ChZn6, 7 = Dhw, 8 = Extra, 9..14 = CoolZn1..CoolZn6, 15..16 = Extra1..Extra2. The bridge uses [<zone>] (heating zone 1 by default).
useCache: false is mandatory
A useCache: true read immediately after any write returns stale cloud-cached values. The bridge's reduced ≤ comfort validation on subsequent writes would then operate on stale data and silently revert the boiler. Always read fresh.
HA-side helpers¶
The bridge listens for state changes on these HA helpers and propagates to the boiler:
| Helper | Effect |
|---|---|
input_number.elco_ch_comfort_temp_set |
CH comfort setpoint via PlantTimeProgBsb/SetTemperature |
input_number.elco_ch_reduced_temp_set |
CH reduced setpoint via PlantTimeProgBsb/SetTemperature |
input_number.elco_dhw_comfort_temp_set |
DHW comfort via PlantHomeBsb/SetData |
input_number.elco_dhw_reduced_temp_set |
DHW reduced via PlantHomeBsb/SetData |
input_select.elco_zone_mode_set |
Zone mode (Protection/Automatic/Reduced/Comfort) via PlantHomeBsb/SetData |
input_select.elco_dhw_mode_set |
DHW mode (Off/On) via PlantHomeBsb/SetData |
input_boolean.elco_holiday_active |
Toggle ON → write slot 0 with from=today, to=helper-date. Toggle OFF → write the empty marker. |
input_datetime.elco_holiday_until |
Return date used by the holiday toggle. |
input_boolean.elco_holiday_use_reduced |
Holiday stays in Reduced (vs fully off) — zoneData.useReducedOperationModeOnHoliday via SetData |
The bridge also publishes a wide entity set (boiler temps, flame, runtime per day, schedule slices per weekday, Riemann-integrated energy + cost) consumed by the Chauffage dashboard, automations and the Energy panel.
Discovering mobile-only endpoints¶
The mobile /api/v2/remote/... paths are not derivable from static analysis of the web's JS bundle — the URL constants are loaded at runtime from a hash-keyed map. To find them, intercept the mobile app's HTTPS traffic with mitmproxy:
# Run on any Linux host on the LAN; no router involvement.
pipx install mitmproxy
mitmdump --listen-host 0.0.0.0 --listen-port 18080 \
--set save_stream_file=/tmp/flows.mitm
On the Android phone:
- Wi-Fi → modify network → Proxy: Manual →
<linux-host-ip>:18080 - Browser →
http://mitm.it→ Android → install the CA (Settings will prompt; name itmitmproxy) - Use the target app while the capture is running
The Remocon-NET Android app trusts user-installed CAs (no certificate pinning). Many Android apps pin, in which case an APK Network-Security-Config patch + re-signing is required — but the user-CA path is worth trying first.
Mobile APIs are often cleaner than web APIs on the same backend
A vendor cloud's mobile app and web UI frequently use different API surfaces on the same backend. The mobile app's API tends to be a cleaner REST layer added later, while the web UI runs against the original (often ASP.NET MVC) endpoints. When integrating with a vendor cloud, capture the mobile-app traffic first — it usually reveals a more pleasant API to target.
What the cloud does not expose¶
Some BSB parameters are present on the boiler but not in the cloud's BsbPlantData / BsbZoneData schemas:
- Boiler flow / return water temperature — not surfaced. The closest available reading is
dhwStorageTemp(the hot-water tank probe). - Burner modulation / current input power — not surfaced. The bridge publishes a synthetic
sensor.elco_heating_power(14 kW when the burner is firing, 0 otherwise) for Riemann-integration into kWh. - Per-day or per-hour consumption history — not available to the
FinalUserrole on this gateway.PlantMetering/*returns 404 (the boiler is provisioned without metering hardware),PlantLog/*is gated to tech/CAT users and returns 302→login, and the standard BSB burner-hour datapoints (8530/8540/8550/8560) returnM2M 502on this firmware. The Remocon-NET mobile app shows no Statistics tab for this gateway.
For these, the only path is a direct BSB serial dongle on the boiler — not via the cloud. None of those is wired up today.
Energy tracking and recovery¶
The Energy panel is fed by a Riemann-integrated sensor.elco_heating_energy_total (kWh) and a derived sensor.elco_heating_cost_period (CHF). The integrator's source is binary_sensor.elco_flame_sensor (burner firing), not binary_sensor.elco_room_heating_is_active — the latter stays on through the entire heating season and would inflate the integral by an order of magnitude.
Long-term statistics for any energy/cost stream can be reset via the recorder WebSocket API. From inside the home-assistant pod:
await ws.send(json.dumps({
"type": "recorder/clear_statistics",
"statistic_ids": [
"sensor.elco_heating_energy_total",
"sensor.elco_heating_energy_period",
"sensor.elco_heating_cost_period",
"sensor.elco_heating_cost_total",
],
}))
The entity's current state is preserved; only the historical hourly/5-min buckets are dropped. The Energy panel re-bootstraps from the current state at the next hourly compile, and accumulation continues from that baseline.
Because the cloud exposes no historical consumption surface, no backfill is possible — energy data starts from when HA was first wired with the correct flame-based integrator.