Skip to content

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.index selects which slot to write (0–7). The bridge always writes slot 0 to mirror the mobile app.
  • old is 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:

{
  "useCache": false,
  "zone": 1,
  "filter": {"progIds": [1], "plant": true, "zone": true}
}

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:

  1. Wi-Fi → modify network → Proxy: Manual → <linux-host-ip>:18080
  2. Browser → http://mitm.it → Android → install the CA (Settings will prompt; name it mitmproxy)
  3. 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 FinalUser role 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) return M2M 502 on 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.