The problem
When trying to change room temperature setpoints (parameters with sh-indoorSpHeat smart home category, e.g. parameter IDs 47751, 47752) via the integration, the API responds with "modified" but the value is never actually applied on the heat pump.
This affects NIBE SMO S40 (and likely other devices with zone-based climate systems).
Root cause
The integration uses the PATCH /v2/devices/{deviceId}/points endpoint for all writable parameters. However, for zone-based temperature setpoints, this endpoint silently accepts the request but does not apply the change.
The myUplink API has a dedicated endpoint for this: PATCH /v2/devices/{deviceId}/zones/{zoneId} which works correctly.
How I confirmed this
1. PATCH via /points — fails silently
curl -X PATCH "https://api.myuplink.com/v2/devices/{deviceId}/points" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"47752":"22"}'
Response: {"47752": "modified"} — but a subsequent GET still returns the old value, and the heat pump is unchanged.
2. PATCH via /zones/{zoneId} — works
curl -X PATCH "https://api.myuplink.com/v2/devices/{deviceId}/zones/3" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"setpointHeat": 22.0}'
This actually changes the setpoint on the heat pump and is confirmed via GET on /smart-home-zones.
3. Zone data from the API
GET /v2/devices/{deviceId}/smart-home-zones returns:
[
{
"zoneId": "2",
"name": "RDC",
"mode": "heat",
"temperature": 20.7,
"setpointHeat": 20.0,
"setpointRangeMin": 15,
"setpointRangeMax": 25,
"isCelsius": true
},
{
"zoneId": "3",
"name": "Etage",
"mode": "heat",
"temperature": 20.5,
"setpointHeat": 20.0,
"setpointRangeMin": 15,
"setpointRangeMax": 25,
"isCelsius": true
}
]
The zone name matches the parameter category field (e.g. "RDC", "Etage").
What I noticed in the code
The integration already has:
- A
Zone class fully implemented in api.py
- A
get_zones() method that calls /smart-home-zones
- A
zones list on the Device class
However:
- The call to
get_zones() is commented out in Device.async_fetch_data():
# self.zones = await self.system.api.get_zones(self.id)
- There is no
patch_zone() method — only patch_parameter() which uses /points
Proposed solution
1. Add a patch_zone method to the MyUplink class:
async def patch_zone(self, device_id, zone_id: str, setpoint_heat: float) -> bool:
"""Update the setpoint of a zone for a device."""
_LOGGER.debug(
"Patch zone %s for device %s with setpointHeat %s",
zone_id, device_id, setpoint_heat,
)
async with self.lock, self.throttle:
resp = await self.auth.request(
"patch",
f"devices/{device_id}/zones/{zone_id}",
data=json.dumps({"setpointHeat": setpoint_heat}),
headers={"Content-Type": "application/json"},
)
resp.raise_for_status()
return resp.status == 200
2. Update Parameter.update_parameter() to route zone parameters:
async def update_parameter(self, value) -> None:
"""Patch parameter if writable."""
if not self.is_writable:
return
if "sh-indoorSpHeat" in self.smart_home_categories and self.category:
zones = await self.device.system.api.get_zones(self.device.id)
for zone in zones:
if zone.name == self.category:
await self.device.system.api.patch_zone(
self.device.id, str(zone.id), float(value)
)
return
await self.device.system.api.patch_parameter(
self.device.id, str(self.id), str(value)
)
The logic: when a parameter has the sh-indoorSpHeat smart home category, we look up the matching zone by name (which corresponds to the parameter's category field), and use the zones endpoint instead of the points endpoint.
Environment
- Device: NIBE SMO S40
- myUplink subscription: Premium (manage)
- Integration version: 1.7.3
- Home Assistant: 2025.x
Additional context
Other writable parameters (e.g. 27233 Quick water heating) work fine via /points. The issue is specifically with zone-based temperature setpoints that require the /zones/{zoneId} endpoint.
The Swagger documentation at https://api.myuplink.com/swagger/index.html confirms both endpoints exist:
PATCH /v2/devices/{deviceId}/points — for general parameter updates
PATCH /v2/devices/{deviceId}/zones/{zoneId} — for zone-specific updates
Thank you for this great integration! Happy to submit a PR if you'd prefer.
The problem
When trying to change room temperature setpoints (parameters with
sh-indoorSpHeatsmart home category, e.g. parameter IDs47751,47752) via the integration, the API responds with"modified"but the value is never actually applied on the heat pump.This affects NIBE SMO S40 (and likely other devices with zone-based climate systems).
Root cause
The integration uses the
PATCH /v2/devices/{deviceId}/pointsendpoint for all writable parameters. However, for zone-based temperature setpoints, this endpoint silently accepts the request but does not apply the change.The myUplink API has a dedicated endpoint for this:
PATCH /v2/devices/{deviceId}/zones/{zoneId}which works correctly.How I confirmed this
1. PATCH via
/points— fails silentlyResponse:
{"47752": "modified"}— but a subsequent GET still returns the old value, and the heat pump is unchanged.2. PATCH via
/zones/{zoneId}— worksThis actually changes the setpoint on the heat pump and is confirmed via GET on
/smart-home-zones.3. Zone data from the API
GET /v2/devices/{deviceId}/smart-home-zonesreturns:[ { "zoneId": "2", "name": "RDC", "mode": "heat", "temperature": 20.7, "setpointHeat": 20.0, "setpointRangeMin": 15, "setpointRangeMax": 25, "isCelsius": true }, { "zoneId": "3", "name": "Etage", "mode": "heat", "temperature": 20.5, "setpointHeat": 20.0, "setpointRangeMin": 15, "setpointRangeMax": 25, "isCelsius": true } ]The zone
namematches the parametercategoryfield (e.g. "RDC", "Etage").What I noticed in the code
The integration already has:
Zoneclass fully implemented inapi.pyget_zones()method that calls/smart-home-zoneszoneslist on theDeviceclassHowever:
get_zones()is commented out inDevice.async_fetch_data():# self.zones = await self.system.api.get_zones(self.id)patch_zone()method — onlypatch_parameter()which uses/pointsProposed solution
1. Add a
patch_zonemethod to theMyUplinkclass:2. Update
Parameter.update_parameter()to route zone parameters:The logic: when a parameter has the
sh-indoorSpHeatsmart home category, we look up the matching zone by name (which corresponds to the parameter'scategoryfield), and use the zones endpoint instead of the points endpoint.Environment
Additional context
Other writable parameters (e.g.
27233Quick water heating) work fine via/points. The issue is specifically with zone-based temperature setpoints that require the/zones/{zoneId}endpoint.The Swagger documentation at https://api.myuplink.com/swagger/index.html confirms both endpoints exist:
PATCH /v2/devices/{deviceId}/points— for general parameter updatesPATCH /v2/devices/{deviceId}/zones/{zoneId}— for zone-specific updatesThank you for this great integration! Happy to submit a PR if you'd prefer.