Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Atlantic Cozytouch for Homey Pro

Control your Atlantic heating, hot water, and air conditioning devices through the Cozytouch cloud platform on your Homey Pro.

Support & device reports: Homey Community thread · Issue tracker

Disclaimer: This app is not affiliated with or endorsed by Atlantic/Groupe Atlantic. It relies on a reverse-engineered cloud API that may change without notice.


Table of Contents

  1. Prerequisites
  2. Installation
  3. How the App Works
  4. App Configuration Page
  5. Atlantic Cozytouch API Reference
  6. Overkiz API Reference
  7. Compatible Devices
  8. Architecture Overview
  9. Capability ID Reference
  10. Homey Flow Integration
  11. Known Limitations
  12. Troubleshooting
  13. Adding Support for New Devices

Prerequisites

  • Homey Pro (firmware >= 5.0) or Homey Pro (Early 2023)
  • An Atlantic Cozytouch account (the same credentials you use in the Cozytouch mobile app)
  • At least one Atlantic device paired to your Cozytouch bridge/gateway
  • Homey CLI installed for development: npm install -g homey

Installation

From source (development)

git clone <this-repo>
cd homey-cozytouch
npm install
homey app run        # Deploy to your Homey for testing
homey app install    # Install permanently

From Homey App Store

Not yet published. Planned for a future release.


How the App Works

High-Level Flow

                                    ┌──────────────────────────┐
                    ┌──────────────►│  CozyTouch (Magellan)    │
                    │  HTTPS/REST   │  apis.groupe-atlantic.com│
┌─────────────────┐ │               └──────────────────────────┘
│   Homey Pro      │─┤                         │
│                  │ │               ┌─────────┴─────────┐
│  ┌────────────┐  │ │               │  Cozytouch Bridge │
│  │ CozyTouch  │  │ │               └─────────┬─────────┘
│  │   App      │  │ │                  │    │    │
│  │  ┌─────┐   │  │ │               Kelud  AC  Boiler (newer)
│  │  │ API │   │  │ │
│  │  │x 2  │   │  │ │               ┌──────────────────────────┐
│  │  └─────┘   │  │ └──────────────►│  Overkiz                 │
│  │  ┌─────┐   │  │    HTTPS/REST   │  ha110-1.overkiz.com     │
│  │  │Drvrs│   │  │                 └──────────────────────────┘
│  │  └─────┘   │  │                          │
│  └────────────┘  │                ┌─────────┴─────────┐
└─────────────────┘                 │  Cozytouch Bridge │
                                    └─────────┬─────────┘
                                       │    │    │
                                    Calypso Zeneo Thermor (older)

Dual Protocol Architecture

Atlantic uses two separate cloud backends:

Protocol Endpoint Devices Auth
CozyTouch (Magellan) apis.groupe-atlantic.com Newer: boilers, towel racks, AC OAuth2 password grant
Overkiz ha110-1.overkiz.com Older: water heaters, Thermor/Sauter Atlantic token -> JWT -> Overkiz login

The app authenticates to both and discovers devices from each during pairing.

Lifecycle

  1. Configuration: User saves Cozytouch credentials in the App Configuration Page. On startup, the app restores sessions for both protocols.

  2. Pairing: Uses Homey's system login_credentials view. If credentials are already in app Settings, pairing skips the form and lists devices for that driver. Otherwise the user logs in; a successful login is saved to Settings. Discovery always queries both CozyTouch and Overkiz; each device is tagged with its protocol. Already-paired devices are excluded by data.id (Homey's default full-data filter is not enough for devices paired before credentials left data — see Settings Storage).

  3. Sync (global): One app setting sync_interval (default 60 s, range 30–300). Each cycle:

    1. For each Overkiz account: POST /setup/devices/states/refresh (refreshStates) — asks the gateway/cloud to refresh device states (same family of call official apps use when opened).
    2. Then poll all paired devices (Overkiz + Magellan) and update Homey capabilities.
  4. Commands: When the user changes a setting, the app routes the command to the correct API. CozyTouch uses writecapability, Overkiz uses exec/apply.

Key Classes

Class File Role
CozyTouchApp app.js App entry point. Dual API instances, credentials, Flow cards, global sync timer.
CozyTouchAPI lib/CozyTouchAPI.js HTTP client for the Magellan API (newer devices).
OverkizAPI lib/OverkizAPI.js HTTP client for the Overkiz API (older / io devices). 3-step auth.
CozyTouchDevice lib/CozyTouchDevice.js Base device class. Protocol-aware polling and command routing.
CozyTouchDriver lib/CozyTouchDriver.js Base driver: Homey login template, Settings credential reuse, combined discovery.
HeaterDevice drivers/heater/device.js Radiators / boilers (incl. Ipala adjustable setpoint).
WaterHeaterDevice drivers/water_heater/device.js Water heater with DHW mode, boost, and away mode.
TowelRackDevice drivers/towel_rack/device.js Towel rack with dual-protocol support (Magellan + Overkiz).
ClimateDevice drivers/climate/device.js Heat pump/AC with HVAC modes, fan, and swing.
PassCozytouchDevice drivers/pass_cozytouch/device.js Atlantic Pass Cozytouch wall modules.
ZoneControlDevice drivers/zone_control/device.js Shogun Zone Control (main unit + heating/cooling zones).
HeatPumpDevice drivers/heat_pump/device.js Pass APC heat pumps: Alféa / Excellia main unit + heating circuits.
API routes api.js Settings page backend: status, test connection, credential management.

App Configuration Page

The app provides a settings page accessible from Homey Settings > Apps > Atlantic Cozytouch.

API Endpoints (Settings Backend)

Method Path Description
GET /api/status Returns current auth status for both protocols
POST /api/test-connection Tests credentials against both APIs, returns device lists
POST /api/save-credentials Saves credentials to Homey settings storage
POST /api/clear-credentials Removes saved credentials

Features

  • Credential management: Save, update, and clear Cozytouch credentials
  • Pairing reuse: Saved credentials are used automatically when adding devices (no re-entry per driver)
  • Connection testing: Test both CozyTouch and Overkiz protocols with a single click
  • Status monitoring: Real-time green/red/gray indicators for each protocol
  • Device discovery: View all devices found on each protocol after a connection test
  • Auto-restore: Credentials persist across app restarts; sessions are automatically re-established
  • Sync interval: App setting sync_interval (30–300 s, default 60) — Overkiz refreshStates then poll all devices

Settings Storage

Credentials are stored using homey.settings (encrypted local storage on the Homey Pro). The key is credentials with the structure { username, password }.

This is the only place the app keeps them. A paired device's data object holds identifiers only (id, accountDeviceId), so a device that gets logged or exported carries no credentials; CozyTouchDevice reads the account from app settings on init. Devices paired before 1.3.7 still hold a copy in their immutable data — the first start after the update moves it into app settings and the app stops reading it. Clearing the account therefore leaves devices unavailable until one is saved again, at which point they start themselves.

Homey's built-in pairing list filters duplicates by comparing the whole data object. That broke for pre-1.3.7 devices (credentials still in data vs identifiers-only candidates), so they could show up again under the cloud default name and be added a second time. From 1.4.4 the driver excludes already-paired devices by matching data.id only (lib/helpers/pairing.js).


Atlantic Cozytouch API Reference

The Atlantic Cozytouch cloud API is not publicly documented. The following is based on reverse engineering from the cozytouch Home Assistant integration.

Base URL

https://apis.groupe-atlantic.com

Authentication

The API uses OAuth2 Resource Owner Password Credentials (ROPC) grant.

Request:

POST /users/token HTTP/1.1
Host: apis.groupe-atlantic.com
Authorization: Basic Q3RfMUpWeVRtSUxYOEllZkE3YVVOQmpGblpVYToyRWNORHpfZHkzNDJVSnFvMlo3cFNKTnZVdjBh
Content-Type: application/x-www-form-urlencoded

grant_type=password&scope=openid&username=GA-PRIVATEPERSON/user@email.com&password=yourpassword

Response:

{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJSUzI1NiIs..."
}

Key details:

  • The Authorization: Basic header uses a fixed client ID (Base64-encoded client credentials).
  • The username must be prefixed with GA-PRIVATEPERSON/.
  • The returned access_token is used as a Bearer token for all subsequent requests.

Endpoints

Device Discovery

GET /magellan/cozytouch/setupviewv2
Authorization: Bearer {access_token}

Returns an array with one setup object containing all devices, zones, and account metadata.

Response structure:

[
  {
    "id": "setup-id",
    "address": { "country": "FR", ... },
    "devices": [
      {
        "deviceId": 12345,
        "name": "My Boiler",
        "modelId": 56,
        "productId": 100,
        "gatewaySerialNumber": "XXXX-XXXX-XXXX",
        "zoneId": 1,
        "tags": [{ "label": "...", "value": "..." }],
        "capabilities": [...]
      }
    ],
    "zones": [...]
  }
]

Read Capabilities (Polling)

GET /magellan/capabilities/?deviceId={deviceId}
Authorization: Bearer {access_token}

Returns an array of capability objects for the device:

[
  {
    "capabilityId": 7,
    "value": "21.5",
    "name": "Current Temperature",
    "type": "temperature",
    "category": "sensor"
  },
  {
    "capabilityId": 2,
    "value": "19.0",
    "name": "Target Temperature",
    "type": "temperature",
    "category": "sensor"
  }
]

Write Capability (Send Command)

POST /magellan/executions/writecapability
Authorization: Bearer {access_token}
Content-Type: application/json

{
  "capabilityId": 2,
  "deviceId": 12345,
  "value": "22.0"
}

Response (HTTP 201):

{
  "executionId": "exec-uuid"
}

Check Execution Status

GET /magellan/executions/{executionId}
Authorization: Bearer {access_token}

Response:

{
  "executionId": "exec-uuid",
  "state": 3
}

States: 3 = completed. The app polls up to 5 times (1 second apart) waiting for completion.

Away Mode (Setup-Level)

PUT /magellan/v2/setups/{setupId}
Authorization: Bearer {access_token}
Content-Type: application/json

{ ... away mode timestamp data ... }

Overkiz API Reference

The Overkiz API is used for older Atlantic/Thermor/Sauter devices. It is the same platform used by Somfy TaHoma.

Base URL

https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI

Authentication (3-step)

Step 1: Get Atlantic access token (same as CozyTouch)

POST https://apis.groupe-atlantic.com/users/token
Authorization: Basic Q3RfMUpWeVRtSUxYOEllZkE3YVVOQmpGblpVYToyRWNORHpfZHkzNDJVSnFvMlo3cFNKTnZVdjBh
Content-Type: application/x-www-form-urlencoded

grant_type=password&scope=openid&username=GA-PRIVATEPERSON/user@email.com&password=yourpassword

Step 2: Exchange for JWT

GET https://apis.groupe-atlantic.com/magellan/accounts/jwt
Authorization: Bearer {access_token_from_step_1}

Returns a JWT token.

Step 3: Login to Overkiz

POST /login
Content-Type: application/json

{"jwt": "eyJhbGciOiJSUzI1NiIs..."}

Endpoints

Method Path Description
GET /setup Full setup with all devices, gateways, zones
GET /setup/devices Device list only
GET /setup/devices/{url}/states Current states for a device
POST /setup/devices/states/refresh Force state refresh
POST /exec/apply Execute a command
GET /exec/current/{id} Get execution status
POST /events/register Register event listener
POST /events/{id}/fetch Fetch state change events

Key DHW States (Water Heater - CETHI_V4)

State Name Description
core:TargetTemperatureState Target water temperature
io:MiddleWaterTemperatureState Mid-tank water temperature
io:DHWModeState DHW operating mode (manualEcoInactive/manualEcoActive/autoMode)
core:BoostModeDurationState Boost duration in days (>0 = active)
io:AwayModeDurationState Away duration ("0" or "always" = away active)
core:MinimalTemperatureManualModeState Min allowed temperature
core:MaximalTemperatureManualModeState Max allowed temperature

Key DHW Commands (CETHI_V4)

Command Parameters Description
setTargetTemperature [temperature] Set target water temperature
setDHWMode ["manualEcoInactive"] Set DHW mode (manual/eco/auto)
setCurrentOperatingMode [{relaunch, absence}] Control boost and away mode
setBoostModeDuration [7] Set boost duration in days
refreshBoostModeDuration [] Refresh boost state

Key Towel Dryer Commands (IC3)

Command Parameters Description
setTowelDryerOperatingMode ["external"] Set mode (standby/external/internal)
setTargetTemperature [temperature] Set target temperature

Key Towel Dryer States (IC3)

State Name Description
core:OperatingModeState Current mode (standby/external/internal/auto)
core:TargetTemperatureState Target temperature
core:TemperatureState Current room temperature

Compatible Devices

Tested Devices

The following devices have been validated with real hardware:

Device Reference Protocol Driver Status
Atlantic Kelud 500W Anthracite Etroit Towel rack CozyTouch (Magellan) towel_rack Fully working (mode, temperature)
Sauter Asama (I2G_Actuator) Towel dryer Overkiz towel_rack Fully working (mode, temperature)
Atlantic Calypso (Ballon Thermodynamique) Thermodynamic water heater Overkiz water_heater Fully working (mode, temperature, boost, away)
Atlantic Pass Cozytouch (ref. 602251) Wall module Overkiz pass_cozytouch Fully working (heating level, on/off)
Shogun Zone Control Main unit + heating/cooling zones Overkiz zone_control Fully working (HVAC mode, zone temps/modes, on/off, tile heat/cool)
Sauter / Thermor Ipala Adjustable-setpoint radiator Overkiz heater Fully working (mode, temperature, on/off); energy, active level and comfort periods since 1.5.0 — a first hardware report (1.5.1) confirmed mode, setpoint, active level and the power estimate; the schedule action is accepted by the cloud, its effect is still being confirmed

Added from user reports, only partly validated on real hardware: Atlantic Alféa Extensa Duo A.I. 5 R32 (Magellan modelId 212; supported through its Overkiz endpoints, paired under heat_pump for the heating circuit and outside probe, and under water_heater for the tank — see the Pass APC heat pump section below. A first hardware report in 1.4.11 confirmed the endpoints are detected; the tank's controls were reworked in 1.4.13 from it) and Calypso connecté 240L (Magellan modelId 1658, productId 62, paired under water_heater — its gateway answers on Cozytouch/Magellan only, the Overkiz side returns No such user account; own capability block since 1.4.12, see Water Heater — Calypso connecté below). See issue #5. Also Alféa Extensa S Duo 8's hot water tank (Magellan modelId 1376, productId 47, paired under water_heater), whose Cozytouch/Magellan capability block differs from every other tank mapped so far — see Water Heater — Alféa Extensa Duo tank below.

Heater / Boiler Driver

Handles gas boilers and thermostats.

Device Type Model IDs Known Products
Gas Boiler 56, 61, 65, 1444 Naema 2 Micro, Naema 2 Duo, Naia 2 Micro, Naia 2 Duo
Thermostat 235, 418 Atlantic thermostats

Capabilities: target temperature, current temperature, heating mode (off/manual/eco+/program), on/off

Towel Rack Driver

Handles electric towel dryers via both protocols.

Device Type Model IDs (Magellan) Overkiz controllableName Known Products
Towel Rack 1381, 1382, 1386, 1543, 1546, 1547, 1551, 1622 io:AtlanticElectricalTowelDryer_IC3_IOComponent Kelud, Sauter Asama, Kaoli

Magellan commands: Cap 7 (HVAC mode: 0=off, 4=heat), Cap 184 (preset: 0=manual, 1=prog), Cap 40 (target temperature)

Overkiz commands: setTowelDryerOperatingMode (standby/external/internal), setTargetTemperature

Capabilities: target temperature, current temperature, heating mode (off/manual/program), on/off

modelId 1388 / 1389 (TESC_0, TESC_1) are not towel dryers — removed in 1.4.11. They were mapped here because they appear next to towel-dryer models, but on an Alféa heat pump account productId 55 / 56 answer with none of the IDs above — only 19, 109, 218, 106000, so the tile showed nothing and warned. 1.4.10 made it worse: the new pairing guidance recommended the Towel Rack tile for them. They are the heating circuits of the ROOM_x zones, on four independent pieces of evidence:

  • the endpoint prefix names them. Upstream maps TESC to ("Heating circuit", 54) (mathieuletyrant/cozytouch-hacs, model.py) — the same MODEL_NAMES table that names DHW and ROOM.
  • 106000 is circuit_driven_by_room, and both report 1: the setpoint of this circuit is held by a room, so it is not a thing to control on its own. Upstream's apply_capabilities deliberately creates no device for such a circuit, because it would duplicate the room.
  • the setpoints track the zones. On one account TESC_0 reads 19=0.0 while ROOM_0 is off (7=0) and TESC_1 reads 19=23.27 while ROOM_1 is in heat (7=4) — a circuit water setpoint, not a room one. 109 is boiler_water_temperature per circuit and differs between the two (22.47 vs 22.02).
  • 1388 and 1389 are absent from that project's 1583-entry model_catalogue.py, while every genuine towel dryer above (1381, 1382, 1386, 1543, 1546, 1547, 1551, 1622) is named there. They belong to the same endpoint/role namespace as 556–570 and 1376, not to the appliance-model namespace.

No capability block is mapped for them: reading 109 into measure_temperature would show boiler water as room temperature — the same mistake fixed for productId 26 in 1.4.8. What the owner controls is the room, which pairs under climate. Devices added under Towel Rack before 1.4.11 are not removed automatically; delete them by hand.

Beyond the model list, pairing on this driver — and on climate, heater and water_heater — now also checks that a Magellan device reports at least one of the capability IDs the driver reads (lib/helpers/capability-support.js). A model that slips through classification no longer becomes a device with nothing on any tile. The check is deliberately permissive: an Overkiz device, a device that announced no capability list, or a driver that maps no IDs all pass, since a device that was never judged has only gone unseen.

Water Heater Driver

Device Type Model IDs (Magellan) Overkiz controllableName Known Products
Water Heater 236, 389, 390, 1369, 1371, 1372, 1376, 1642, 1644, 1645, 1656, 1657, 1658, 1966 io:AtlanticDomesticHotWaterProductionV2_CETHI_V4_IOComponent, io:AtlanticDomesticHotWaterProductionMBLComponent (MBL) Atlantic Calypso (incl. Calypso connecté, 1658), AQUEO ACI HYB (389 / 390, productId 7), Zeneo, Vizengo, Lineo, Égéo (MBL)

Overkiz commands: setDHWMode (manualEcoInactive/manualEcoActive/autoMode), setTargetTemperature, setCurrentOperatingMode (away/boost), setBoostModeDuration

Magellan capabilities: Cap 1 (mode: 0=manual, 3=eco+, 4=prog), Cap 2 (target temperature), Cap 3 (on/off), Cap 5 (boost), Cap 7 (current temperature), Cap 10 (away), Cap 160/161 (temperature range)

Capabilities: target temperature (30-65C), current temperature, heating mode, boost toggle, away mode

Note: the mode picker differs per protocol, because the two protocols do not expose the same modes. Overkiz tanks get Off / Manual / Eco / Auto (no Auto on Égéo / MBL, where Auto is Eco); Magellan tanks get Off / Manual / Eco / Program, since capability 1 has a prog value and no auto value. A Magellan product with no on/off capability (AQUEO ACI HYB) drops Off: it is always on and its mode is what runs it.

Note: The CETHI_V4 water heater has no real on/off command. "Off" is simulated via away mode. Shower count is only controllable from the Cozytouch phone app.

Magellan capabilities, AQUEO ACI HYB (productId 7 — modelId 389 / 390): this product answers on none of the IDs above. Its own block is Cap 87 (mode: 0=manual, 3=eco+, 4=prog), Cap 231 (target temperature, mirrored on Cap 22), Cap 266 (tank top temperature; 264 bottom, 265 middle, 267 average), Cap 165 (boost), Cap 227 (away: 0=off, 1=on, 2=booked), Cap 105301/105304 (setpoint range). It has no on/off capability at all: the tank is always on and driven by its mode, so the mode picker offers Manual / Eco / Program without Off.

Magellan capabilities, Alféa Extensa Duo tank (productId 47 — modelId 1376, "Calypso Split"): also answers on none of the default IDs. Cap 87 (mode: 0=manual, 3=eco+, 4=prog), Cap 231 (target temperature, mirrored on Cap 22), Cap 111 (current tank temperature), Cap 86 (on/off), Cap 165 (boost), Cap 105301/105300 (setpoint min/max). Away mode (Cap 226) is a start/stop timestamp pair on this product, not a switch, so it is not offered.

Note: capability IDs are resolved per productId from the device store — see WATER_HEATER_CAP_IDS_BY_PRODUCT in lib/constants/cozytouch-mappings.js. A product that is not in that table falls back to the default IDs and, if it matches none of them, says so in the log (see Troubleshooting).

Climate Driver (Heat Pump / AC)

Device Type Model IDs Known Products Modes
Heat Pump 76 Loria Duo R32 (standard) Off, Heat
Heat Pump 211 Loria Duo R32 (advanced) Off, Heat, Auto
Air Conditioning 557-561 Takao M3 series Off, Auto, Cool, Heat, Fan Only, Dry

Capabilities: target temperature (with separate heat/cool setpoints), current temperature, HVAC mode, fan speed (AC only: auto/low/medium/high), swing position (AC only: up/middle-up/middle-down/down), on/off

Pass Cozytouch Driver

Wall modules (io-homecontrol) under driver pass_cozytouch.

Device Type Overkiz controllableName Known Products
Pass Cozytouch io:AtlanticElectricalHeaterIOComponent Atlantic Pass Cozytouch (ref. 602251)

Overkiz commands: setHeatingLevel (comfort / eco / frost / off style levels) — no temperature setpoint on the module itself.

Capabilities: heating level / mode, on/off

Shogun Zone Control Driver

Dedicated driver zone_control (Overkiz Pass APC Zone Control stack).

Role Overkiz controllableName / widget What it controls
Main unit AtlanticPassAPCZoneControlMainComponent / AtlanticPassAPCZoneControl Global HVAC for the system
Heating/cooling zone AtlanticPassAPCZoneControlZoneComponent / AtlanticPassAPCHeatingAndCoolingZone Per-room circuit
Zone temperature sensor AtlanticPassAPCZoneTemperatureSensor Linked to zone — not paired as a Homey device

Capabilities (main): cozytouch_hvac_mode, on/off

Capabilities (zones): target temperature, measure temperature, zone mode (cozytouch_heating_mode: off/manual/prog), on/off, read-only thermostat_mode mirrored from the main unit (Homey tile heat/cool colors)

Pass APC Heat Pump Driver

Dedicated driver heat_pump (Overkiz Pass APC heat pump stack): Atlantic Alféa Extensa / Excellia, Hydrapac and the other air/water heat pumps that speak the same io:PassAPC* protocol as the Shogun.

Overkiz splits one heat pump into several endpoints of the same io://gateway/deviceId#index stack. Endpoint indexes differ per product, so every role is detected on its component (or widget) name and siblings are linked by name, never by a fixed offset:

Role Overkiz controllableName / widget Driver What it controls
Main unit AtlanticPassAPCHeatPumpMainComponent / AtlanticPassAPCHeatPump heat_pump System mode (cozytouch_hvac_mode), on/off, outside temperature
Heating circuit AtlanticPassAPCHeating[AndCooling]ZoneComponent / AtlanticPassAPCHeatingZone, AtlanticPassAPCHeatingAndCoolingZone heat_pump Setpoint, circuit mode (off/manual/prog), on/off
Hot water tank (Duo) AtlanticPassAPCDHWComponent / AtlanticPassAPCDHW water_heater On/off (Off / Auto), eco-or-comfort setpoint, boost — see below
Outside probe AtlanticPassAPCOutsideTemperatureSensor — Read by the main unit, not paired
Circuit probe AtlanticPassAPCZoneTemperatureSensor — Read by its circuit, not paired
Energy sensor TotalElectricalEnergyConsumptionSensor — Not exposed yet

Capabilities (main unit): cozytouch_hvac_mode, on/off, measure_temperature (outside temperature, only when the stack has an outside probe)

Capabilities (circuits): target temperature (16–30 °C), measure temperature, circuit mode (cozytouch_heating_mode: off/manual/prog), on/off, and read-only thermostat_mode mirrored from the main unit only on a reversible stack

Command names come from the device, not from the model. The Atlantic range keeps the protocol but not the command names — one circuit takes setHeatingTargetTemperature, another only advertises setDerogatedTargetTemperature, and a heating-only unit has no setHeatingCoolingAutoSwitch at all. lib/helpers/overkiz-commands.js snapshots each endpoint's definition.commands into the device store at pairing time and picks the first name the device actually accepts; a control whose command is missing fails with a message naming it instead of sending something the heat pump refuses. Cooling support is detected the same way, so an Extensa is not offered cool/dry/auto modes it cannot do.

Hot water tank of a Duo (AtlanticPassAPCDHWComponent)

Added from the Water Heater tile, not the Heat Pump one. The Heat Pump tile pairs the main unit and the heating circuits and finds something, so it never says the tank exists — a user who adds a Duo from there sees a heating circuit and an outside temperature and no hot water. This is also why the 1.4.10 "try another device type" guidance, which only runs when a driver finds nothing, does not catch it.

What an Extensa Duo A.I. 5 R32 reports for it (Test Connection, app 1.4.11):

io:PassAPCDHWConfigurationState=cumulated   io:PassAPCDHWModeState=peakAndOffPeakScheduling
io:PassAPCDHWProfileState=eco               core:DHWDerogationAvailabilityState=available
core:ComfortTargetDHWTemperatureState=52    core:EcoTargetDHWTemperatureState=40
core:TargetDHWTemperatureState=40           core:BoostOnOffState=off    core:DHWOnOffState=on

It is not an Overkiz tank like the Calypso, and until 1.4.13 it was driven as one. What it has is an on/off switch, a boost, and two setpoints of which the tank publishes the one in force as TargetDHWTemperatureState — 40 here, because the profile is eco (the comfort setpoint is 52). It has no tank temperature, no absence state, and a "mode" that names a scheduling type rather than a Manual / Eco / Auto choice. Home Assistant's Overkiz integration drives the same component the same way (on/off + boost, eco/comfort setpoint) and is the one public reference for what these tanks accept.

Tile Behaviour since 1.4.13
Mode picker Off / Auto only. Off is setDHWOnOffState off; Auto switches the tank on and leaves it on its own schedule. Manual, Eco and Program do not exist on this tank and are refused with a readable error rather than sent as setPassAPCDHWMode values nothing shows it takes
Target temperature Reads TargetDHWTemperatureState. Writes the eco setpoint in eco profile and the comfort one otherwise (always comfort during boost), then asks the tank to republish it with the refresh…TargetDHWTemperature commands it advertises. Writing the setpoint the tile is not showing was why the slider snapped back
Boost core:BoostOnOffState / setBoostOnOffState
Temperature Not offered. The component publishes no tank temperature, and an empty tile reads as a broken sensor. The only candidate is Magellan cap 111 (49.9 °C in the same report), not confirmed to be the tank
Away Not offered unless the tank advertises an absence command; switching the tank off is what away means on it. Removed at init from a tank paired earlier that has none

Auto going back to Off, Boost doing nothing (1.5.2). A second owner of the same model reported that Auto selected in the picker was Off again when it was reopened, and that Boost did nothing. No log came with it, so the cause is not settled; what the code did is. After every command the device re-polls three seconds later (_schedulePostCommandRefresh), and the cloud goes on reporting the previous core:DHWOnOffState / core:BoostOnOffState for a while — a minute or more on the Ipala, whose log shows it. That first re-poll wrote the old value back over what had just been set. Unlike the setpoint, which the tank is asked to republish (refresh…TargetDHWTemperature), nothing asks it to republish the on/off or boost state. The handler now holds what it wrote for up to two minutes (PendingWrites, shared with the Ipala), so the picker and the Boost toggle stay where the user put them.

What that does not tell is whether the tank applies the commands at all: setDHWOnOffState and setBoostOnOffState are the names Home Assistant drives it with, and no report has yet listed what this tank advertises. Two log lines are there for that:

  • [cozytouch_heating_mode] "auto" was sent but the device still reports "off" 120 s later; showing what it reports — the cloud accepted the command and the tank never applied it (or never republished it). Without this line a command that is only slow and one that was ignored look the same.
  • Commands this tank accepts: …, or No command list stored for this tank (paired before commands were recorded). The list is recorded at pairing, so a tank paired before 1.4.13 sends every command unchecked, and removing it and adding it again is what records the real list (after which a command the tank lacks fails with its name).

The tank's temperature is a separate matter and unchanged: the component publishes none, so there is no tile.

The mode, profile and configuration have no tile, so the app log carries them once per distinct combination: Tank reports mode "peakAndOffPeakScheduling", profile "eco", configuration "cumulated". Command names are picked from what the tank advertises, as everywhere on Overkiz; Test Connection now lists them (see below).

The heat pump is also announced on Magellan (modelId 212 for the Extensa Duo A.I.). It is deliberately not mapped in MODEL_TYPES: pairing it there would create a second tile for the same appliance, on a protocol whose capability IDs for this product are unknown. The Overkiz endpoints are the supported ones.

Known gap — Alféa Extensa S Duo 8 main unit (modelId 2303 / 2327): on at least one such account the appliance is announced only on Magellan, the same way the Calypso connecté's tank is Magellan-only (see above). That account reports the Overkiz side as "no such user account" rather than a credential failure, so it exists only on Magellan and no amount of re-entering credentials can reach Overkiz.

The Magellan dump for 2303/2327 has since been captured (Test Connection prints it for every device on the account, unmapped ones included — see A device was added but shows no values), and it says there is nothing thermostatic on those two endpoints:

  • 2303 / productId 54 — outside temperature (119), water temperature (109), two energy counters (57, 59, in Wh; the 164 bitmask advertises electricity-for-heating, electricity-for-DHW and both production counters), wifi signal (179), serial/model (88, 94, 335) and an away-mode switch (152, with its timestamp pair on 222). No cap 7, no cap 40.
  • 2327 / productId 58 — pump start/hour counters (25, 26, 28, 29), water pressure (100), boiler water and exhaust temperature (109, 116). Diagnostics only. Upstream's PRODUCT_ID_RUNS gives productId 58 to modelId 1391 as well, which MODEL_TYPES knows as GENERATOR_0 — so this second "Alfea Extensa S" endpoint is the generator (the heat pump's own hydraulic module), which is exactly what that capability set describes.

What the owner actually controls lives on the endpoints, and those already pair: ROOM_0 / ROOM_1 under climate, DHW_0 under water_heater. A future sensor-only device could expose the appliance's outside temperature, water pressure, energy counters and away mode; there is nothing else to expose.

Its hot water tank (modelId 1376) works via water_heater — see Water Heater — Alféa Extensa Duo tank.

Ipala (heater driver)

Adjustable-setpoint radiators stay on the heater driver (no dedicated driver).

Device Type Overkiz controllableName Known Products
Adjustable setpoint heater io:AtlanticElectricalHeaterWithAdjustableTemperatureSetpointIOComponent Sauter / Thermor Ipala

Capabilities: target temperature, measure temperature, heating mode, on/off, plus the three below, which are added to the device the first time the radiator reports the data behind them (so a radiator paired by an earlier version gets them without being re-added).

The Overkiz stack of one Ipala is five endpoints; the app reads three of them:

Endpoint Component Used for
#1 AtlanticElectricalHeaterWithAdjustableTemperatureSetpointIOComponent Mode, setpoint, level, rated power, working rate, weekly schedule
#2 io:TemperatureInCelciusIOSystemDeviceSensor Room temperature
#5 io:CumulatedElectricalEnergyConsumptionIOSystemDeviceSensor Energy counter

(#3 is a window contact and #4 an occupancy sensor, neither exposed.) The counter is found by component name among the radiator's siblings, not by its index.

Capability Source Notes
meter_power (kWh) core:ElectricEnergyConsumptionState on the counter endpoint, in Wh ÷ 1000 Cumulative, so Homey Energy and Insights can use it as is. Resolution on the units seen is 1 kWh.
measure_power (W) io:PowerState × io:CurrentWorkingRateState ÷ 100 An estimate, flagged isApproximated: the radiator publishes its rating (750 / 1000 W…) and the share of time its element is on, not a measured power. Reads 0 W while the radiator is off — a dump of one in standby still carried a working rate of 100 %.
cozytouch_heating_level io:TargetHeatingLevelState Read-only. The level the radiator is applying now: comfort, eco, off, frostprotection, comfort-1/-2. On a programmed radiator this is what changes at the start and end of a comfort period.

Weekly schedule. core:TimeProgramState holds, per weekday, three {start, end} windows (00:00–00:00 = unused). A window is a comfort period; outside every window the radiator is at its eco level. The Flow action Set comfort periods reads the program, replaces the chosen day (or Monday–Friday / weekend / every day) and sends the whole program back with setTimeProgram; the other days are returned exactly as reported. Input is 06:30-08:00, 17:00-22:00 (three periods at most, on the hour or half hour, ending by 23:30); none or empty means eco all day.

Not verified on hardware: nobody publishes a setTimeProgram call, so the parameter format (the same list of seven day objects the state returns) is the most likely one, not a known one. The payload is logged (Sending setTimeProgram: …) so a refusal can be diagnosed from one report. The first hardware report (1.5.1) ran the action on Sunday: the cloud accepted the command without an error (a 98 ms Flow run) and the payload in the log was the radiator's own program with that day replaced, but whether the radiator then follows it is still unconfirmed. Afterwards the Cozytouch app showed "the programming source of the PROG mode is set to external control, switch it to Internal programming (PROG menu)". Whether that message comes from the command, and whether the radiator ignores the schedule while it shows, is not known; core:OperatingModeState is now logged once per value (Operating mode reported by the radiator: "…") because the app reads both internal and external as Program. core:EcoRoomTemperatureState reads 3.5 on every dump next to io:SetpointLoweringTemperatureInProgModeState 3.5, which looks like a setback rather than a temperature, so there is no eco-temperature action yet; the target-temperature tile writes the comfort temperature.

Found with the first hardware report (1.5.1):

  • Mode is the operating mode, never the level. Until 1.5.1 a radiator at its eco level was shown as Manual, whatever core:OperatingModeState said. A programmed radiator is at that level for most of the day, so Program could not be seen on a tile at all (prog -> manual and comfort -> eco in the same poll). The mode and on/off tiles now follow the operating mode only; the level has its own tile.
  • The cloud lags a command by up to a minute. Switching a radiator off read back as off -> manual -> off over two polls, because the first poll after the command still returned the old state. PendingWrites keeps the value just sent (mode, on/off, setpoint) for two minutes or until the radiator reports it, so the tiles and the Heating mode changed trigger no longer follow the stale read.
  • Readings update about every ten minutes on the cloud side. The room temperature, the working rate and so the power estimate each changed on a ten-minute beat in the log (the temperature at :11, :21, :31…, the rate at :17, :27, :37…, later :36, :46), whatever the sync interval. Homey reads them every cycle; the cloud has nothing newer to give, which is also why a power change shows up minutes after the radiator changed.
  • refreshStates was refused on every cycle (429 QUOTA_EXCEEDED, a request a minute for the whole log) — see Sync and the Overkiz refresh quota.
  • The tile colour is Homey's. The device page stayed orange after Off, which points to Homey colouring a thermostat from its target-temperature value alone (not confirmed): the Ipala keeps reporting its manual setpoint while off or in eco, and the app has no say in the colour. Showing the setpoint the radiator is applying (eco or comfort) instead would need a state this app does not read yet — something like io:EffectiveTemperatureSetpointState, to be looked for in the Test Connection report of a programmed radiator.
  • Open: the temperature probe read about 1 °C higher for the few minutes the radiator was off (20.16 → 21.19 → 21.29 → 20.22 °C), with no other radiator moving; the app passes on what endpoint #2 reports. Absence mode, a temperature offset and the eco/comfort temperatures were asked for and are not read — they need the states from a Test Connection report to be mapped.

Hub/Gateway (Not Directly Controlled)

Model IDs Notes
556, 1353 Cozytouch bridge - detected but not exposed as Homey device

AC Controllers (Detected, Not Yet Mapped)

Model IDs Notes
562-570 AC controller modules - detected during discovery but not yet fully mapped

Alféa Extensa Duo A.I. 5 R32 (modelId 212, Not Yet Mapped)

Reported by a user whose account announces this appliance as one Magellan device rather than as the ROOM_x / DHW_0 / TESC_x / GENERATOR_0 endpoint set another Extensa account announces — so none of the per-endpoint blocks above apply to it. modelId 212 is absent from MODEL_TYPES, it answers type UNKNOWN, and no driver claims it. 211 (Loria Duo R32 advanced) is its neighbour in the table, not a substitute.

What it reports (productId 2, app 1.4.10, abridged):

Alfea Extensa Duo A.I. 5 R32 | modelId 212 | productId 2 | type UNKNOWN
  1=0, 3=2, 4=0, 7=0, 8=3, 9=0, 17=18.0, 19=null, 22=40, 23=52, 39=40, 41=19.0, 73=2, 86=-, 87=-,
  96=24.0, 97=26.0, 109=31.40625, 111=51.46875, 116=36.640625, 117=25.0, 119=19.3, 154=Plancher,
  165=-, 169=100.0, 171=35.0, 172=18.0, 184=0, 188=3, 100022 absent, 100406=51501240

Not added to MODEL_TYPES yet, deliberately — CLIMATE_CAP_IDS would be wrong for it in a way that pairing cannot detect:

  • Cap 2 (TARGET_TEMP_HEAT) does not exist on this product, and neither do 160–163 (the setpoint limits). Classified as a heat pump it would pair with a target-temperature tile that reads empty and 404s on write.
  • The defaults that are present are not obviously the right ones. Cap 1 is heating_mode on boilers and hvac_mode on climate devices; 7 carries the mode rather than a temperature on the ROOM_x blocks, and here 1=0 and 7=0 both. 73=2 is a third candidate. Getting this wrong is the 1.4.8 bug.
  • The setpoint is likewise ambiguous: 22/39=40 and 171=35.0 read as circuit-water setpoints (154 is Plancher — underfloor), while 41=19.0 and 17=18.0 read as room setpoints.
  • 100022 (supported_system_operating_modes), the bitmask the mode picker should be built from, is not reported by this product, so the mode list cannot be derived from the payload either.

86/87/165 (DHW on/off, DHW mode, boost) are null on this product despite it being a Duo with a tank.

Settled by the Overkiz report (1.4.11). The same Test Connection printed this appliance's Overkiz endpoints, and the two protocols agree to the digit, which pins the Magellan IDs without a second capture:

Magellan cap Value Overkiz endpoint Value
117 26.3 AtlanticPassAPCZoneTemperatureSensor core:TemperatureState 26.3 — room probe, zone 1
119 22.5 AtlanticPassAPCOutsideTemperatureSensor core:TemperatureState 22.5 — outside probe
57 / 59 / 60 4000 / 14000 / 17000 heating / DHW / total ElectricEnergyConsumptionState 4000 / 14000 / 17000
23 / 22 52 / 40 ComfortTargetDHWTemperatureState / EcoTargetDHWTemperatureState 52 / 40
154 Plancher core:NameState of the heating circuit Plancher

Not confirmed: 111 (51.5 → 49.9 °C between two reports). By analogy with productId 47, where it is the tank temperature, it probably is — and it is the only place this appliance's tank temperature exists at all, since the Overkiz tank publishes none. Using it would mean reading a Magellan capability on behalf of an Overkiz device (matched on cap 88 = core:ProductModelNameState), which has not been done.

So the supported route for this appliance is Overkiz, and modelId 212 stays out of MODEL_TYPES.


Architecture Overview

File Structure

homey-cozytouch/
├── app.js                              # App entry point, credential restore, Flow cards
├── app.json                            # Homey manifest (drivers, capabilities, flows)
├── api.js                              # Settings page API routes
├── package.json
│
├── lib/
│   ├── CozyTouchAPI.js                 # CozyTouch/Magellan REST API client
│   ├── OverkizAPI.js                   # Overkiz REST API client (3-step auth)
│   ├── CozyTouchDevice.js              # Base device: handler dispatch, polling, auth
│   ├── CozyTouchDriver.js              # Base driver: pairing, combined discovery
│   ├── constants/
│   │   ├── cozytouch-mappings.js       # CAP_IDS + mode maps per device type
│   │   └── overkiz-mappings.js         # STATES, COMMANDS, level/DHW maps, helpers
│   └── helpers/
│       ├── discovery-report.js         # Names found devices in pairing errors
│       ├── magellan-capabilities.js    # Capability payload: lookup, dump, API errors
│       ├── overkiz-commands.js         # Which commands an endpoint advertises
│       ├── overkiz-device.js           # Widget / controllableName detection
│       ├── pairing.js                  # Exclude already-paired devices by data.id
│       ├── pending-writes.js           # Hold a value just sent until the cloud reports it
│       ├── refresh-gate.js             # Back off the Overkiz refresh after a 429 quota refusal
│       ├── time-program.js             # Ipala weekly comfort periods: parse + rewrite
│       └── water-heater-modes.js       # Modes a tank accepts, per protocol
│
├── settings/
│   └── index.html                      # App config page (credentials, status)
│
├── drivers/
│   ├── heater/
│   │   ├── device.js                   # Thin shell: creates handler, wires listeners
│   │   ├── driver.js                   # Filters for boiler/thermostat/Ipala models
│   │   ├── handlers/
│   │   │   ├── cozytouch.js            # CozyTouch cap IDs, mode values, API writes
│   │   │   ├── overkiz.js              # Overkiz states, commands, heating levels
│   │   │   └── overkiz-adjustable-setpoint.js  # Ipala-style radiators
│   │   └── assets/                     # icon.svg + store images (small/large/xlarge)
│   │
│   ├── water_heater/
│   │   ├── device.js                   # Thin shell + boost/away listeners
│   │   ├── driver.js                   # Filters for water heater models
│   │   ├── handlers/
│   │   │   ├── cozytouch.js            # Magellan cap IDs (mode, boost, away)
│   │   │   ├── overkiz.js              # Overkiz DHW commands, absence mode
│   │   │   ├── overkiz-mbl.js          # Égéo / modbuslink tanks (setDHWMode)
│   │   │   └── overkiz-pass-apc.js     # Tank of a Pass APC heat pump (Alféa Duo)
│   │   └── assets/
│   │
│   ├── climate/
│   │   ├── device.js                   # Thin shell + fan/swing listeners
│   │   ├── driver.js                   # Filters for heat pump/AC models
│   │   ├── handlers/
│   │   │   ├── cozytouch.js            # HVAC modes per model, fan, swing
│   │   │   └── overkiz.js              # Simple heat on/off
│   │   └── assets/
│   │
│   ├── towel_rack/
│   │   ├── device.js                   # Thin shell: delegates to handler
│   │   ├── driver.js                   # Filters for towel rack models
│   │   ├── handlers/
│   │   │   ├── cozytouch.js
│   │   │   └── overkiz.js
│   │   └── assets/
│   │
│   ├── pass_cozytouch/
│   │   ├── device.js
│   │   ├── driver.js
│   │   ├── handlers/
│   │   └── assets/                     # Pass module illustration + matching icon
│   │
│   ├── zone_control/
│   │   ├── device.js
│   │   ├── driver.js
│   │   ├── constants.js
│   │   ├── handlers/
│   │   └── assets/                     # Répartiteur + remotes illustration + icon
│   │
│   └── heat_pump/
│       ├── device.js                   # Thin shell: main unit vs circuit role
│       ├── driver.js                   # Filters the Pass APC heat pump stack
│       ├── constants.js
│       ├── handlers/
│       │   ├── overkiz-main.js         # System mode, outside probe
│       │   └── overkiz-zone.js         # Circuit setpoint, mode, on/off
│       └── assets/

Pairing uses Homey's system `login_credentials` template (no per-driver HTML).

├── locales/
│   ├── en.json
│   └── fr.json
│
├── assets/
│   ├── icon.svg                        # App icon
│   └── capabilities/
│       └── heating-level.svg           # Icon of the Active level capability
│
├── tests/
│   └── driver-icons.test.js            # Guideline 1.6 checks on every driver icon.svg
│
└── tools/
    └── generate-driver-images.py       # Redraws the driver store images (not shipped)

Driver store images (guideline 1.4) are flat product illustrations on white, one set per driver, generated by python3 tools/generate-driver-images.py. Driver icons (guideline 1.6) are separate currentColor SVGs that mirror those devices; tests/driver-icons.test.js guards the Homey rules.

Data Flow

User Action (Homey UI / Flow)
        │
        ▼
  registerCapabilityListener()     ← drivers/{type}/device.js
        │
        ▼
  this._handler.setMode(value)     ← delegates to protocol handler
        │
        ├─── CozyTouch handler ──► ctx.setCapValue(capId, value)
        │                                   │
        │                                   ▼
        │                          POST /magellan/executions/writecapability
        │
        └─── Overkiz handler ───► ctx.executeCommand(cmd, params)
                                            │
                                            ▼
                                   POST /exec/apply

Polling Flow

setInterval (every 60s)
        │
        ▼
  this._handler.updateState()     ← lib/CozyTouchDevice.js
        │
        ├─── CozyTouch handler ──► GET /magellan/capabilities/?deviceId={id}
        │                                   │
        │                                   ▼
        │                          ctx.setCapability() for each mapped cap
        │
        └─── Overkiz handler ───► GET /setup/devices/{url}/states
                                            │
                                            ▼
                                   ctx.setCapability() for each mapped state

Capability ID Reference

These numeric IDs are used internally by the Atlantic API to identify device properties. They are sent as capabilityId in API requests.

Per product, not per model: the IDs below are the ones observed on supported products. Another product of the same family may implement a different set — writing an ID it does not have returns NoCapabilityImplementationFound (product-level) or UnknownCapabilityId (the API does not know that ID at all). The app dumps the capability list of each Magellan device once per run so an unknown product can be mapped from a log; see Troubleshooting.

Common Capabilities (All Devices)

Cap ID Name Type Description
1 Mode int Heating mode or HVAC mode (meaning varies by device type)
2 Target Temperature float Temperature setpoint (heating)
3 On/Off bool Power switch (1=on, 0=off)
7 Current Temperature float Measured temperature sensor
160 Min Temperature float Minimum allowed setpoint
161 Max Temperature float Maximum allowed setpoint

Water Heater Specific

Cap ID Name Type Description
5 Boost bool Boost mode toggle
6 Eco bool Eco mode toggle
10 Away Mode bool Away/vacation mode — rejected by the API (UnknownCapabilityId) on the products seen so far; use the per-product ID below

Water Heater — AQUEO ACI HYB (productId 7, modelId 389 / 390)

This product shares no capability ID with the table above. IDs mapped from the list the device reports, cross-checked against gduteil/cozytouch (capability.py), which names modelId 390.

Cap ID Name Type Description
87 Heating mode int 0=manual, 3=eco+, 4=prog (same values as cap 1)
231 Target temperature float Setpoint; mirrored on cap 22 (target_temperature_dhw)
105301 / 105304 Setpoint min / max float Range for cap 231 (cap 160/161 absent on this product)
266 Tank top temperature float Used as the device's current temperature (264 bottom, 265 middle, 267 average)
165 Boost bool Boost toggle (same ID as towel racks)
227 Away mode int 0=off, 1=on, 2=booked but not started; timestamps in cap 226
— On/off — Does not exist: the tank is always on, its mode drives it

Reported but not exposed yet: 59 (energy, in Wh), 99 (electric backup running), 179 (Wi-Fi signal), 258 (tank capacity, L), 268/270 (V40 water available/capacity), 271 (hot water available, %), 283 (off-peak hours), 245–251 (weekly program).

Water Heater — Alféa Extensa Duo tank (productId 47, modelId 1376)

Another product with its own block, distinct from both the default IDs and the AQUEO one above. Mapped from a user-submitted diagnostic log, cross-checked against gduteil/cozytouch, which names modelId 1376 "Calypso Split".

Cap ID Name Type Description
87 Heating mode int 0=manual, 3=eco+, 4=prog (same values as cap 1)
231 Target temperature float Setpoint; mirrored on cap 22 (target_temperature_dhw)
86 On/off bool domestic_hot_water switch
111 Current temperature float dhw_temperature
165 Boost bool Boost toggle (same ID as the AQUEO tank and towel racks)
105301 / 105300 Setpoint min / max float Range for cap 231
— Away mode — Not offered: cap 226 is a start/stop timestamp pair, not a boolean switch

Since 1.4.11 the away tile is not created at all on this product, and is removed from a tank paired before then. Offered anyway, the toggle answered "This Cozytouch water heater has no away capability" every time it was touched. Overkiz tanks keep theirs — they all have one, through their own handler.

Cap 87 reports 2 on this tank, and nothing names it. The app knows 0, 3 and 4; upstream's reference table names only those three for modelId 1376 as well, so 2 cannot be named without guessing. Until 1.4.11 the value was dropped in silence, which left the mode picker showing whatever it last held — one tank spent three days displaying a mode it was not in, with nothing in the log to say so. It is now logged once per distinct value, so a single diagnostic report is enough to add it. The real fix is to read cap 105011 (supported_dhw_modes, a bitmask) instead of assuming the value set: this tank reports 32001 = manual|boost|absence|scheduled_absence|antilegionella|smart_grid|on_off — no auto, no prog, which means the mode picker currently offers modes the tank itself says it does not have.

Water Heater — Calypso connecté (productId 62, modelId 1658)

Same family as the two blocks above, and the same symptom: the generic IDs (1, 2, 3, 5, 7, 10, 160, 161) do not exist on this product, so a tank paired before 1.4.12 showed no temperature, no mode, no boost or away state, and every command was refused. Mapped from a user-submitted diagnostic log (the tank reports 84 capabilities), cross-checked against gduteil/cozytouch.

Cap ID Name Type Description
87 Heating mode int 0=manual, 3=eco+, 4=prog (same values as cap 1)
231 Target temperature float Setpoint; mirrored on cap 22 (target_temperature_dhw)
253 / 252 Setpoint min / max float The Cozytouch app's own user target bounds (50 / 62 on the reported tank; 105301 / 105300 hold the same values)
265 Tank middle temperature float Used as the device's current temperature — see below
165 Boost bool Boost toggle
227 Away mode int 0=off, 1=on, 2=booked but not started; timestamps in cap 226
— On/off — Not offered: see below

Current temperature. The tank reports a middle probe (265) and the tank average (267) but no top probe (266). The middle one is the hottest reading it has, which is what comes out of the tap; 267 is the average over all probes, and stays available if that turns out to be the better tile. Other models filed under productId 62 that do report a top probe still read the middle one.

No Off. Cap 86 (domestic_hot_water) is reported, but supported_dhw_modes (cap 105011, a bitmask) reads 16133 = manual|auto|boost|scheduled_boost|absence|scheduled_absence|antilegionella|smart_grid — no on_off bit, unlike the Extensa Duo tank's 32001. Mapped, Off would only be a command the tank refuses, and cap 86 would be written ahead of every mode change. If a tank of this product turns out to switch off, mapping ON_OFF: 86 is a one-line change.

Mode picker. Like the Extensa Duo tank, it still offers Manual / Eco / Program, while 105011 lists auto and no prog. Which mode value 87 reports for each bit is not established (the reported tank is in 0, manual), so the picker is not narrowed yet.

Reported but not exposed yet: 59 (energy, in Wh), 99 (electric backup running), 179 (Wi-Fi signal), 258 (tank capacity, L), 268/270 (V40 water available/capacity), 271 (hot water available, %), 245–251 (weekly program), 234 (boost setpoint).

Climate — Alféa Excellia / Extensa heating zones (productId 26–30, modelId 557–561)

Same per-product story as the two tanks above, on the climate side. These are not air conditioners: they are the heating zones of an Alféa heat pump — ROOM_0 (modelId 557) and ROOM_1 (modelId 558) — which fall in the range MODEL_TYPES calls AC and so land on the climate driver. They answer on the towel-rack block, not the AC one. None of 1, 2, 4, 8, 9 exist on them, and cap 7 carries the mode, not a temperature: read as the current temperature it reported the mode enum, so a room at 21.97 °C showed as 4 °C on ROOM_0 (1.4.8), and a zone in heat mode showed a flat 4 °C on ROOM_1 (1.4.10).

Mapped from two user-submitted reports — an Alféa Excellia M DUO diagnostic log and an Alféa Extensa S Duo 8 Test Connection report — cross-checked against the same dump in gduteil/cozytouch#63 (identical modelId/productId pairs) and against TOWEL_RACK_CAP_IDS, which already uses this block.

Cap ID Name Type Description
7 HVAC mode int 0=off, 4=heat — not the current temperature (same as towel racks)
40 Target temperature float Heating setpoint; cap 17 carries the applied one (7 °C frost protection when off)
117 / 118 Current temperature float Room temperature, z-indexed per zone: 117 on ROOM_0, 118 on ROOM_1
177 Target temp cool float target_cool_temperature; unreachable on a heating-only unit
160 / 161 Setpoint min / max float Same IDs as the default block
— Fan / swing — Do not exist: mapped to null since 1.4.11, so no tile is offered

No room probe means no temperature. 117/118 are reported only when a room sensor is installed. The Excellia account sends 117=21.97; the Extensa S account, on the same productId 26, sends neither 117 nor 118 — it is weather-compensated, driven by the outside probe (cap 119 on the appliance) and a per-zone heating curve (cap 192: 65 °C for its radiator zone, 35 °C for its underfloor zone). Such a device leaves measure_temperature empty. That is correct: an empty tile, not a wrong one.

Reported but not exposed yet: 73 (available thermostat modes), 153 (heating status), 154 (zone name, e.g. «Chauffage», «Pièces de vie»), 157/158 (setpoint override), 166 (system operating mode — a bitmask, 21 = off|heat, not 21 °C), 181 (service in progress, mirrors cap 7), 294 (setpoint step), 303 / 100320–100333 (weekly program), 352–357 (presence/absence setpoints).

ROOM_2–ROOM_4 (modelIds 559/560/561) map to productId 28/29/30, from the vendor's own model catalogue (the generated PRODUCT_ID_RUNS table in mathieuletyrant/cozytouch-hacs, model_product_ids.py, which also confirms every productId observed on a real account: 557→26, 558→27, 1376→47, 1388→55, 1389→56, 2303→54, 2327→58). They get the same block as 26/27 minus the temperature: 117 and 118 are thermostat_temperature_z1/_z2 and the series stops there — 119 is the outside probe, not zone 3 — so a third zone leaves measure_temperature unmapped, which reads as an empty tile. On the AC default (cap 7) it would have reported the mode enum as a temperature: the 1.4.8 bug, on a three-zone house.

No fan and no swing, confirmed by the devices themselves. Cap 100022 (supported_system_operating_modes) reads 29 on these zones. Against upstream's HVAC_MODE_BITS — 1=off, 6=auto, 8=cool, 16=heat, 128=fan, 256=dry — that is off|auto|heat with neither the fan nor the dry bit. Both are mapped to null since 1.4.11, so no picker is offered and a zone paired earlier has its two pickers removed at startup.

557–561 stay classified as AC: reclassifying the modelIds would change the tile of anyone with a genuine Takao AC, and would need a capability migration on every paired device. The per-product blocks make the classification harmless.

Still unfixed (candidates for a later release): the hvac mode picker offers cool, dry and fan_only on these zones, which cap 100022 plus cap 73 (available_thermostat_modes = 2, heating only) both say they do not have.

Climate Specific (Heat Pump / AC)

Cap ID Name Type Description
4 Fan Mode int Fan speed: 0=auto, 1=low, 2=medium, 3=high
8 Target Temp Cool float Temperature setpoint for cooling modes
9 Swing Mode int Air direction: 0=up, 1=mid-up, 2=mid-down, 3=down
11 Quiet Mode bool Silent operation toggle
12 Program int Program schedule: 0=basic, 1=programmed
13 Program Override bool Temporary override of scheduled program
14 Activity bool Activity indicator (1=active)
162 Min Temp Cool float Minimum cooling setpoint
163 Max Temp Cool float Maximum cooling setpoint

Heating Mode Values (Cap ID 1 - Boilers & Water Heaters)

API Value Mode Description
0 Manual Manual temperature control
3 Eco+ Energy-saving mode
4 Program Follow scheduled program

HVAC Mode Values (Cap ID 1 - Climate Devices)

Varies by model. Below are the known mappings:

Default (most heat pumps):

API Value Mode
0 Off
4 Heat

Model 211 (Loria advanced):

API Value Mode
0 Off
1 Heat
2 Auto

Models 557-561 (Takao AC):

API Value Mode
0 Off
1 Auto
3 Cool
4 Heat
7 Fan Only
8 Dry

On an Alféa ROOM_x zone these same modelIds are a heating zone, and cap 7 — not cap 1 — carries the mode with only 0=off / 4=heat. See Climate — Alféa Excellia / Extensa heating zones.

Named from the vendor's own tables (not all mapped yet)

Cross-referenced from mathieuletyrant/cozytouch-hacs (capability_table.py, const.py, model.py), which carries names for the numeric IDs. Listed here because they are what the next fixes are built from, and because two of this app's own names look wrong against them.

Cap ID Upstream name Notes
19 temperature_setpoint On a TESC_x circuit, the circuit water setpoint held by its room
109 boiler_water_temperature Per circuit, not one value per appliance: the two TESC_x differ
119 outside_temperature The appliance's outside probe — not a third zone's room probe
106000 circuit_driven_by_room 1 = this circuit's setpoint is held by a room, so it is not a device
100022 supported_system_operating_modes Bitmask: 1=off, 6=auto, 8=cool, 16=heat, 128=fan, 256=dry
105011 / 168 supported_dhw_modes Bitmask of the modes a tank actually has
252 / 253 target_temperature_max / _min The Extensa tank reports 60.0 / 47.0 here
164 energy_consumption_supported This app calls it MODE_STATUS on towel racks — likely misnamed
172 away_mode_temperature This app calls it ECO_TEMP on towel racks — likely misnamed

Unmapped and unnamed on both sides: 15, 17, 75, 192, 100100, 105301.

Known discrepancy — the Extensa tank's setpoint slider. The app reads its range from 105301/105300 (50–65 °C); the tank also reports 253/252 = 47.0/60.0, and 105300 is water_temperature_limit, a safety ceiling rather than a settable maximum. The slider is therefore likely wrong at both ends. Not changed yet: it needs a second account to confirm which pair a product actually honours.


Homey Flow Integration

Triggers

Trigger Description Token
Temperature changed Fires when measured temperature changes temperature (number)
Heating mode changed Fires when heating mode changes (heaters, tanks, towel racks, heat pump circuits) mode (string)
Pass Cozytouch mode changed Fires when the Pass module level changes level (string)
HVAC mode changed Fires when a climate or heat pump main unit mode changes mode (string)
Zone Control HVAC mode changed Fires when the Shogun main unit mode changes mode (string)
Boost turned on / off Fires when boost starts or stops (water heater) —
Away mode turned on / off Fires when away mode starts or stops (water heater) —

Actions

Action Description Parameters
Set heating mode Change the heating mode mode: off, manual, eco_plus, prog, auto
Set HVAC mode Change the HVAC mode (climate, heat pump main unit) mode: off, heat, cool, auto, dry, fan_only
Set Zone Control HVAC mode Change the Shogun main unit mode mode: off, heat, cool, dry, auto
Set Zone Control zone mode Change a Shogun zone mode mode: off, manual, prog
Set Pass Cozytouch mode Change the Pass module level level: off, frostprotection, eco, comfort-2, comfort-1, comfort
Turn boost on or off Start or stop boost (water heater) state: on, off
Turn away mode on or off Start or stop away / absence mode (water heater) state: on, off

Target temperature has no app card: Homey's built-in Set the target temperature action works on every device that exposes it.

Conditions

Condition Description Parameters
Heating mode is... Check current heating mode (incl. heat pump circuits) mode: off, manual, eco_plus, prog, auto
Pass Cozytouch mode is... Check the Pass module level level: off, frostprotection, eco, comfort-2, comfort-1, comfort
Zone Control HVAC mode is... Check the Shogun main unit mode mode: off, heat, cool, dry, auto
Zone mode is... Check a Shogun zone mode mode: off, manual, prog
Boost is on Check whether boost is running (water heater) —
Away mode is on Check whether away mode is on (water heater) —

Modes a device has no command for are rejected with an error instead of being silently ignored: a water heater accepts Off / Manual / Eco (+ Auto except on Égéo MBL tanks, where Auto is the same as Eco), not Program; a heat pump circuit accepts Off / Manual / Program, and a heating-only heat pump refuses Cool / Dry / Auto.

Flow Examples

"When I leave home, set water heater to away mode":

  • Trigger: Homey location (left home zone)
  • Action: Turn away mode on (water heater)

"Set heating to eco+ at night":

  • Trigger: Time is 22:00
  • Action: Set heating mode to eco_plus

Known Limitations

Physical remote / local changes vs cloud (Overkiz)

Some Overkiz / Cozytouch devices do not always push state changes to the cloud when you use a wall remote or local control. The official Cozytouch app often forces a status refresh when opened; Homey reads the same cloud API and asks for a refreshStates on each sync cycle (unless Overkiz is refusing it, see Sync and the Overkiz refresh quota), but:

  • If the gateway has not reported the new setpoint yet, Homey (and Cozytouch until refresh) stay outdated.
  • Opening Cozytouch can make the value appear after a few seconds even when Homey’s previous polls looked stale — that is expected platform behaviour, not Homey using a different backend.

This is a known Overkiz limitation (also documented for Home Assistant):

Is refreshStates still useful? Yes: it is the right cloud call to request a gateway refresh (same family as the official apps). It helps when the bridge responds, and keeps Homey aligned once the cloud has the truth. It cannot invent updates the gateway never sent.

Sync and the Overkiz refresh quota

Overkiz limits the refresh request (POST /setup/devices/states/refresh) and answers 429 QUOTA_EXCEEDED ("Too many requests, try again later") when it is asked too often — possibly from a quota shared with the official Cozytouch app, which asks for refreshes of its own. One report (an Ipala, 1.5.0) logged that refusal on every sync cycle for ninety minutes: a request a minute, none accepted, one error line each.

Since 1.5.1 a refused refresh is held back — 2 minutes the first time, doubling up to 15 — and asked for normally again once it is accepted. The log says so (Overkiz refreshStates refused (quota exceeded); not asked again for N min, then Overkiz refreshStates accepted again). The devices are still read on every cycle, since reading states is not what is limited: nothing is lost but a refresh that was not being granted, and readings change when the cloud has new ones (see the ten-minute beat noted under Ipala). lib/helpers/refresh-gate.js holds the pacing; app.js consults it before every refresh.

Device shows as unavailable / rate limits

Calling Overkiz too aggressively can hit quotas (QUOTA_EXCEEDED). Prefer a moderate sync_interval (e.g. 60–120 s).


Troubleshooting

"Authentication failed"

  • Verify your email and password work in the official Cozytouch mobile app.
  • The username must be the email used for your Cozytouch account.
  • The API prefixes your username with GA-PRIVATEPERSON/ automatically.

"Unable to connect to Cozytouch"

  • Check your Homey's internet connection.
  • The Atlantic API (apis.groupe-atlantic.com) may be temporarily down.
  • Tokens expire; the app auto-refreshes on HTTP 401, but a restart may help.

A diagnostic log full of Poll failed lines

A Cozytouch access token lives about eight hours, so roughly three times a day the first poll after expiry returns 401 and is recovered half a second later by re-authenticating. Until 1.4.11 that was logged as an error before the recovery was attempted, against a counter that never reset — a perfectly healthy device reached Poll failed (#8) with nothing wrong with it, and a submitted log read as a failing app. A recovered 401 is now one info line, Access token had expired; re-authenticated and polled again, and Poll failed (#n in a row) counts consecutive failures only, so the number means "this device is broken right now" rather than "this app has been running a while".

Adding a device says the account is empty, and Test Connection says it is not

Fixed in 1.4.11. The same eight-hour token expiry as above, on the one code path that had no recovery for it.

isAuthenticated() on both APIs reports only that a token was obtained at some point — CozyTouchAPI.authenticate() discards the expires_in it is handed — and discoverDevices() guarded its login behind exactly that check. So an expired token was treated as a live session, getSetup() answered 401 on both protocols, each failure was swallowed into one log line, and pairing ended on a hardcoded, unlocalized "No devices found on either Cozytouch or Overkiz protocols".

It hits new users specifically, which is why it survived several releases:

  • On a Homey with paired devices the poll cycle renews the token every ~8 h on its way past a 401 (CozyTouchDevice._handlePollError), so discovery always found a fresh one.
  • On a Homey with no paired device there is no poll cycle, so nothing ever renews it. An Extensa Duo owner installed the app, came back the next day, and reached discovery holding a 38-hour-old token.
  • getCozyTouchApi() / getOverkizApi() hand back the cached instance for as long as it claims to be authenticated, and nothing cleared the token on 401 — so the poisoned instance was sticky until an app restart.
  • Test Connection gave a false all-clear. It builds a fresh API instance and authenticates unconditionally, never touching the cached ones. It reported 13 devices, twice, while pairing stayed broken — which is why this was reported as an integration problem with the appliance rather than as a login problem.

Since 1.4.11 each protocol's discovery runs through withFreshSession() (lib/helpers/session-retry.js): a 401 drops the session explicitly (invalidateSession()), logs in again and retries once. The retry is capped at one, so genuinely wrong credentials cannot loop. The two outcomes are also no longer reported as the same thing — an account that answered and holds nothing this app can use keeps 1.4.10's pairing guidance (errors.no_protocol), while two protocols that both refused to answer say so (errors.discovery_failed), in the user's own language.

On 1.4.10 and earlier the workaround is to restart the app and pair within the next few hours.

Homey setpoint/mode wrong after using the wall remote

  • Check the Cozytouch app without changing anything: if it is also wrong until you wait/refresh, the cloud is behind — see Known Limitations.
  • After Cozytouch shows the correct value, Homey should catch up on the next sync cycle (sync_interval).

Device shows as unavailable

  • The sync interval may be too short, causing rate limiting. Try increasing Sync interval in the app settings (e.g. 120 s).
  • The device may be offline in the Cozytouch bridge.

Temperature values seem wrong

  • Capability IDs may differ for your specific device model. Check Homey developer logs (homey app log) to see raw capability data.
  • Some models report temperature in tenths of degrees.

A device was added but shows no values (Cozytouch / Magellan)

Magellan answers per product, not per model family: a device this app classifies as a water heater can implement a different capability set than the IDs its handler reads. When that happens every read is empty and every write returns a 404 such as There is no implementation for capability Id 2 on product Id 7 or Capability Id '10' not found (seen on the AQUEO ACI HYB VM, modelId 390 / productId 7).

The app makes that case visible instead of showing a healthy-looking empty device:

  • The first capability read of each device is dumped once per app run: Magellan capabilities (modelId 390, productId 7, capabilities endpoint): 1 "Mode"=0, 117=52.5, …
  • When the per-device endpoint answers with an empty list, the app retries from the setup view (setupviewv2), which carries the values for some products, and says which source it used.
  • A poll where not one mapped capability matched logs what the device does report and puts a warning on the tile.
  • A refused write reports "This control does not exist on this device model." instead of raw API JSON.

Send that dump line (diagnostic report or homey app log) in an issue — it is what the capability map for a new product is built from.

My device is not discovered

  • Only devices with known modelId / Overkiz controllableName mappings are shown. Check Compatible Devices.

  • Check you are on the right device type first. Pairing is per driver, and an appliance's endpoints are spread across several of them: an Alféa heat pump's zones pair under Heat Pump / AC, its tank under Water Heater, and Heat Pump (Alféa / Pass APC) is Overkiz-only — on a Magellan-only account it finds nothing even though the rest of the account works. When a driver finds nothing it now names the device types that would pair what was discovered, and lists separately what no driver supports:

    No compatible devices found in your Cozytouch account — Add these from another device type instead:
    Water Heater (DHW_0 DEFAULT), Heat Pump / AC (ROOM_0, ROOM_1)
    — Not supported yet: Alfea Extensa S (modelId 2303 / productId 54), TESC_0 DEFAULT (modelId 1388 /
    productId 55), TESC_1 DEFAULT (modelId 1389 / productId 56), Alfea Extensa S (modelId 2327 / productId 58)
    

    A device the guidance names has to actually work once added, or the advice is worse than none. Up to 1.4.10 this list recommended Towel Rack for TESC_0 / TESC_1, which added two devices with nothing on any tile; they now appear on the "not supported yet" side, where they belong.

    The app log carries the same two lists uncapped, plus every discovered device with its modelId / productId.

  • An Alféa Duo's hot water is added from Water Heater, not Heat Pump. The heat pump tile pairs the heating circuit and the outside probe and says nothing about the tank, so a Duo owner who adds it from there sees no hot water at all. Add Water Heater and look for the …#2 endpoint (AtlanticPassAPCDHWComponent).

  • To add a new model, see Adding Support for New Devices.

All of the above only covers a device that pairs: the dump comes from CozyTouchDevice, which an unclaimed modelId never reaches. That was the blocker on every unmapped product — the capability list needed to add support for it was only obtainable from a device that already worked.

App settings → Test Connection now closes that loop. setupviewv2 carries each device's capabilities alongside its identity, so the settings page prints, for every device on the account, paired or not:

Cozytouch (Magellan) — 7 device(s) — app 1.4.11

TESC_0 DEFAULT | modelId 1388 | productId 55 | type UNKNOWN
  19=30.00000000000000000000, 109=26.84000000000000000000, 218=0, 106000=1

type UNKNOWN marks a modelId absent from MODEL_TYPES — the ones that cannot pair at all. Both IDs are printed because capability blocks are keyed on productId, not on the model family. This is a superset of the pairing error's describeDiscoveredDevices() line, which names devices without their capabilities.

Since 1.4.11 the Overkiz half of the account is reported the same way (describeOverkizAccount()), which it was not before: it listed only label, uiClass and type, and uiClass is not an identifier support can be keyed on — HeatingSystem covers both an Alféa Pass APC heating zone and a plain electrical heater. An Extensa Duo owner sent a report showing twelve Overkiz devices and not one actionable line. controllableName, widget, the deviceURL and each device's core: / io: states are now included — and, since 1.4.13, the commands it accepts (definition.commands), because states say what a product reports and only the command list says what it can be told. Every Overkiz handler picks its commands from that list, so without it a mapping can only be checked against another integration's idea of the product:

Overkiz — 12 device(s) — app 1.4.13

Zone 1 | io:AtlanticPassAPCHeatingZoneComponent | uiClass HeatingSystem | widget AtlanticPassAPCHeatingZone | type HEATER
  io://1234-5678-9012/8912345
  core:TargetTemperatureState=20.5, core:OnOffState=on
  commands: setPassAPCHeatingMode, setDerogatedTargetTemperature, refreshTargetTemperature

Both halves land in the one textarea, shown whenever either protocol answered — keyed off the Magellan half alone, the box stayed hidden on an account whose devices all sit on Overkiz, which is precisely the account that cannot be diagnosed any other way.

The report contains device names and identifiers, so the settings page says to glance over it before posting it publicly.


Adding Support for New Devices

Step 1: Identify the Model ID

Enable debug logging or check the Homey app logs during pairing. The setup response contains all devices with their modelId and productId.

Step 2: Add the Model ID

In lib/CozyTouchAPI.js, add the new modelId to the appropriate array in MODEL_TYPES:

const MODEL_TYPES = {
  GAZ_BOILER: [56, 61, 65, 1444],           // Add new boiler models here
  WATER_HEATER: [236, 389, 390, ...],       // Add new water heater models here
  // etc.
};

Step 3: Add HVAC Modes (if climate device)

If the device has non-standard HVAC modes, add a mapping in HVAC_MODES:

const HVAC_MODES = {
  // ...
  999: { 0: 'off', 1: 'heat', 5: 'cool' },  // New model
};

Step 4: Map Capabilities

If the device uses non-standard capability IDs, update the CAP_IDS object in lib/constants/cozytouch-mappings.js for that device family. The app logs the raw capability list of every Magellan device once per run, which is what you map from:

Magellan capabilities (modelId 390, productId 7, capabilities endpoint): 1 "Mode"=0, 117=52.5, 160=50, 161=65

A device whose payload matches none of the mapped IDs also logs None of the capabilities this app reads exist on this device (…) and warns on its tile, so an unsupported product is visible without reading the whole log.

Step 5: Test

homey app run

Monitor the logs to verify capabilities are being read and written correctly.


Credits

  • API reverse engineering based on gduteil/cozytouch (Home Assistant integration), whose capability.py / model.py are also the cross-reference for the per-product Magellan capability IDs (AQUEO ACI HYB, productId 7)
  • Built with the Homey Apps SDK v3

License

Copyright 2026 NicolasYDDER

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

See LICENSE for the full text.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages