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.
- Prerequisites
- Installation
- How the App Works
- App Configuration Page
- Atlantic Cozytouch API Reference
- Overkiz API Reference
- Compatible Devices
- Architecture Overview
- Capability ID Reference
- Homey Flow Integration
- Known Limitations
- Troubleshooting
- Adding Support for New Devices
- 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
git clone <this-repo>
cd homey-cozytouch
npm install
homey app run # Deploy to your Homey for testing
homey app install # Install permanentlyNot yet published. Planned for a future release.
┌──────────────────────────┐
┌──────────────►│ 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)
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.
-
Configuration: User saves Cozytouch credentials in the App Configuration Page. On startup, the app restores sessions for both protocols.
-
Pairing: Uses Homey's system
login_credentialsview. 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 bydata.id(Homey's default full-datafilter is not enough for devices paired before credentials leftdata— see Settings Storage). -
Sync (global): One app setting
sync_interval(default 60 s, range 30–300). Each cycle:- 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). - Then poll all paired devices (Overkiz + Magellan) and update Homey capabilities.
- For each Overkiz account:
-
Commands: When the user changes a setting, the app routes the command to the correct API. CozyTouch uses
writecapability, Overkiz usesexec/apply.
| 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. |
The app provides a settings page accessible from Homey Settings > Apps > Atlantic Cozytouch.
| 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 |
- 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) — OverkizrefreshStatesthen poll all devices
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).
The Atlantic Cozytouch cloud API is not publicly documented. The following is based on reverse engineering from the cozytouch Home Assistant integration.
https://apis.groupe-atlantic.com
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=yourpasswordResponse:
{
"token_type": "Bearer",
"access_token": "eyJhbGciOiJSUzI1NiIs..."
}Key details:
- The
Authorization: Basicheader uses a fixed client ID (Base64-encoded client credentials). - The username must be prefixed with
GA-PRIVATEPERSON/. - The returned
access_tokenis used as a Bearer token for all subsequent requests.
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": [...]
}
]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"
}
]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"
}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.
PUT /magellan/v2/setups/{setupId}
Authorization: Bearer {access_token}
Content-Type: application/json
{ ... away mode timestamp data ... }The Overkiz API is used for older Atlantic/Thermor/Sauter devices. It is the same platform used by Somfy TaHoma.
https://ha110-1.overkiz.com/enduser-mobile-web/enduserAPI
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=yourpasswordStep 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..."}| 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 |
| 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 |
| 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 |
| Command | Parameters | Description |
|---|---|---|
setTowelDryerOperatingMode |
["external"] |
Set mode (standby/external/internal) |
setTargetTemperature |
[temperature] |
Set target temperature |
| State Name | Description |
|---|---|
core:OperatingModeState |
Current mode (standby/external/internal/auto) |
core:TargetTemperatureState |
Target temperature |
core:TemperatureState |
Current room temperature |
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.
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
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
TESCto("Heating circuit", 54)(mathieuletyrant/cozytouch-hacs,model.py) — the sameMODEL_NAMEStable that namesDHWandROOM. 106000iscircuit_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'sapply_capabilitiesdeliberately creates no device for such a circuit, because it would duplicate the room.- the setpoints track the zones. On one account
TESC_0reads19=0.0whileROOM_0is off (7=0) andTESC_1reads19=23.27whileROOM_1is in heat (7=4) — a circuit water setpoint, not a room one.109isboiler_water_temperatureper 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.
| 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
productIdfrom the device store — seeWATER_HEATER_CAP_IDS_BY_PRODUCTinlib/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).
| 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
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
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)
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.
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: …, orNo 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/productId54 — outside temperature (119), water temperature (109), two energy counters (57,59, in Wh; the164bitmask 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 on222). No cap 7, no cap 40.2327/productId58 — pump start/hour counters (25,26,28,29), water pressure (100), boiler water and exhaust temperature (109,116). Diagnostics only. Upstream'sPRODUCT_ID_RUNSgivesproductId58 tomodelId1391 as well, whichMODEL_TYPESknows asGENERATOR_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.
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
ecolevel was shown as Manual, whatevercore:OperatingModeStatesaid. A programmed radiator is at that level for most of the day, so Program could not be seen on a tile at all (prog -> manualandcomfort -> ecoin 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 -> offover two polls, because the first poll after the command still returned the old state.PendingWriteskeeps 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. refreshStateswas 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
#2reports. 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.
| Model IDs | Notes |
|---|---|
| 556, 1353 | Cozytouch bridge - detected but not exposed as Homey device |
| Model IDs | Notes |
|---|---|
| 562-570 | AC controller modules - detected during discovery but not yet fully 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_modeon boilers andhvac_modeon climate devices;7carries the mode rather than a temperature on theROOM_xblocks, and here1=0and7=0both.73=2is a third candidate. Getting this wrong is the 1.4.8 bug. - The setpoint is likewise ambiguous:
22/39=40 and171=35.0 read as circuit-water setpoints (154isPlancher— underfloor), while41=19.0 and17=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.
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.
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
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
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) orUnknownCapabilityId(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.
| 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 |
| 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 |
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).
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.
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).
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.
| 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 |
| API Value | Mode | Description |
|---|---|---|
| 0 | Manual | Manual temperature control |
| 3 | Eco+ | Energy-saving mode |
| 4 | Program | Follow scheduled program |
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.
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.
| 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) | — |
| 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.
| 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.
"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
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):
- Home Assistant – Overkiz · API limits · “Device state changes are not broadcasted for all devices”
- Community note (Atlantic Cozytouch): values may update rarely, or after a hard refresh when opening the vendor app — HA community thread
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.
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.
Calling Overkiz too aggressively can hit quotas (QUOTA_EXCEEDED). Prefer a moderate sync_interval (e.g. 60–120 s).
- 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.
- 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 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".
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.
- 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).
- 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.
- 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.
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.
-
Only devices with known
modelId/ OverkizcontrollableNamemappings 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
…#2endpoint (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.
Enable debug logging or check the Homey app logs during pairing. The setup response contains all devices with their modelId and productId.
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.
};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
};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.
homey app runMonitor the logs to verify capabilities are being read and written correctly.
- API reverse engineering based on gduteil/cozytouch (Home Assistant integration), whose
capability.py/model.pyare also the cross-reference for the per-product Magellan capability IDs (AQUEO ACI HYB, productId 7) - Built with the Homey Apps SDK v3
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
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.