Danfoss Ally TRV working with remote temp sensor

Just hooking on to this thread, as I am getting a new heater/heatpump in three weeks and will also install TRV’s in each room, which I want to control from HA. My idea is to have a few rooms with external sensors, and in the less used rooms I will just rely on the TRV internal sensor. I mostly heat only the living room, but I want to be able to heat the other rooms individually when needed.
I currently use ZHA with a Sonoff , and from what I have gathered on the forum, currently there aren’t many options for TRV’s that can work on external sensors. The Danfoss/Popp ones look good though. I have a few questions:

  1. Is the combination of ZHA and Danfoss Ally TRV’s a good way to go if I want to achieve a reliable setup? If not, are there other TRV’s that are more reliable? When I say reliable I mean that the hardware doesn’t break and the connection has a high uptime. Small inaccuracies in the temperature are less important to me. Are there better options if I switch to Z2MQTT? That would be a bit of work since I have about 30 devices already and a bunch of custom automations but if I am going to switch this would be the time

  2. In the setup described in this thread, what happens if you manually set a setpoint with the knob? Is that setpoint then related to the internal temperature sensor or does it work the same as setting the setpoint remotely?

You should paste the yaml code in a ```yaml CODE ``` block to make it readable and properly indented:strong text

alias: Big room sensor temperature
sequence:
  service: zha.set_zigbee_cluster_attribute
  data:
    cluster_type: in
    ieee: 04:87:27:ff:fe:fa:92:aa
    endpoint_id: 1
    cluster_id: 513
    attribute: 16405
    value: {{ (states(“sensor.tz3000_mxzo5rhf_ts0201_temperature”) | float * 100) | round(0)}}
mode: single

The following should be more appropriate:

alias: Big room sensor temperature
sequence:
  - service: zha.issue_zigbee_cluster_command
    data:
      cluster_type: in
      ieee: 04:87:27:ff:fe:fa:92:aa
      endpoint_id: 1
      cluster_id: 513
      attribute: 16405
      value: >-
        {{ (states("sensor.tz3000_mxzo5rhf_ts0201_temperature") | float * 100) |
        round(0) }}
mode: single

There are a few types of sonoff usb coordinator sticks.

If https://better-thermostat.org/ does the job, then any TRV can be transformed - not tested.

It does the job for me, but I use zha-toolkit as well with zha for more features. I’ve shared my blueprints/automations/scripts for Danfoss Ally there.

I have not tested other ones. I can tell that I had to reset(battery removal/entry)/reconfigure about 4 Danfoss TRVs out of 7 in 1.25 years.

There is only one active setpoint on the TRV which is the one you see on the TRV, that you can also change with the knob if you did not block that in the configuration), is related to the selected reference temperature: the external room sensor if you enabled that or the internal sensor if the external room sensor is not enabled or stopped providing data since 3 hours.
When you change it using the knob, the new setpoint value is sen to the zigbee coordinator/home assistant and updates in the UI.

Thanks for the info, very helpful!

If I understand that correctly, it can’t work with external sensors directly under ZHA, but you can use the integration to read the external sensor, and send an offset setpoint to the TRV. That works I guess, but it’s not quite the same.

Is it possible to directly read and/or write the valve position?

From what I understand (without reading any detail), Better thermostat will send a temperature setpoint to the thermostat (including TRVs) that will compensate for offsets with a room temperature sensor, window openings, etc. and possibly in a dynamic manner (the offset is not always the same).

You can read the valve position of the Danfoss Ally (pi_heating_demand) but I did not manage to set the valve position. I know a Zigbee TRV where you can set the valve position, but it is only for sale in B2B and not marketed - and it also supports a remote temperature sensor directly (end-device-to-end-device reports).
The Danfoss Ally allows a report configuration for this value which I am tracking on some valves:

I am not happy about how it regulates my home-office - it’s kind of the “russian mountains” (translated from french/roller coaster in plain english). The remote temperature is the lower edge of the orange zone - the bottom graph shows the valve position - it’s an old cast iron radiator and the TRV does not manage the lag very well:

Yeah, that doesn’t look like a well-tuned system. Do you get access to the feedback parameters (PID or whatever they use) ?

Right now all I have is an on-off signal to the boiler so I expect to get at least some improvement even if it isn’t perfect. Main thing for me is to choose the right TRV now. I am considering these and Aqara E1’s mostly ATM. E1 only has external sensor support in Z2M right now, but on Github I can see it’s in the pipeline for ZHA as well.
Then there is Tuya and all the clones of it, I’m not too sure about that, there must be a reason why they are cheaper. Same for Sonoff. I like cheap stuff sometimes but in this case I’d rather invest in something a bit higher in quality. I also saw a reasonable deal on Bosch TRV’s , that’ a brand with a good reputation but the ZHA compatibiltiy isn’t as good AFAICT.

The TRV seems to regulate better with modern radiators that do not have as much heat capacity - but the temperature is constant where I have those, so the TRV has to cope with smaller temperature variations.

Personnaly I would not use Aqara - no good experience with the devices I tested.

They started interacting with the external sensor! Thank you!

As I had some issues with external set temps, I stumbled over this very helpful Danfoss Ally document and thread about multi radiator setup.

Thought it’s a good idea to leave it here as well.

In an effort to understand the official danfoss TRV specs, which weren’t clear at all to me, and after having tried a bunch of variations I’ve come up with the following interpretation which seems to work for me and makes more sense to me. I’ve only tested since a week or so in an unstable environment (renovations). It also looks to me it takes a while (several days - a week? for the TRVs to estimate room inertia I guess) for the heating curve to settle down. Obviously I could be completely wrong! Oh, very important, I also changed the Thermostat vertical orientation to true because of the positioning of the TRV. If somebody has a better interpretation or a better way to phrase which really and verifiably works please let me know!!

uncovered TRV external measured room sensor update with external temperature:

  1. by external temperature change event: every external temp absolute change bigger or equal to 0.1C compared to the TRV’s ‘external measured room sensor’ value and at least every 3hours (timer reset) since last TRV’s ‘external measured room sensor’ update time.
    OR
  2. by interval: between 30min and 3hours.

covered TRV external measured room sensor update with external temperature:

  1. by external temperature change event: every external temp absolute change bigger or equal to 0.1C compared to the TRV’s ‘external measured room sensor’ value and at least every 30mins (timer reset) since last TRV’s ‘external measured room sensor’ update time.
    OR
  2. by interval: between 5min and 30min.

what seems to work best for me is the uncovered by temperature with load balancing between 3 radiators enabled and implementing the danfoss load balancing specs in a script (not just purely the average of the sensors). By interval every 30mins seems to work ok as well ( have that on a different floor so I can compare :slight_smile: ). I’m testing covered by temperature change in a different room but that doesn’t seem great in my specific case.

here’s yesterday’s curve for the room with 3 radiators, load balancing, by temperature event, seems to still be settling, target temp is 19.5 during the day and 18 at night. At first glance average variations from the setpoint seem to be around ± 0.2C on average.
image

[edits to refine wording a bit to hopefully make things a bit clearer]

@surreallo, would it be possible to share you script? I would like to see how to implement load balancing with multiple radiators.

sure, it’s not pretty, dunno too much about HA scripting syntax and HA messed up the formatting a bit. If I did it properly it should be following the load balancing usecases as described in this document https://assets.danfoss.com/documents/202524/AU417130778872en-000101.pdf , section 2.2. it runs at 15 minute intervals.

alias: 2F-SCR-TRV update average load Living - logic
sequence:
  - service: number.set_value
    data_template:
      value: >
        {% set ns = namespace(sum = 0, count = 0, avg = 0) %} {% if
        (states('sensor.2f_trv_living1_load_estimate') | float > -500) %} 
         {% if  ((as_timestamp(now()) 
          - as_timestamp(states.sensor['2f_trv_living1_load_estimate'].last_updated  ) ) | float | round(0)
          < 5400 ) %} 
            {% set ns.sum = ns.sum + states('sensor.2f_trv_living1_load_estimate') | float %}
            {% set ns.count = ns.count + 1 %}
          {% endif %}
        {% endif %} {% if (states('sensor.2f_trv_living2_load_estimate') | float
        > -500) %} 
         {% if  ((as_timestamp(now()) 
          - as_timestamp(states.sensor['2f_trv_living2_load_estimate'].last_updated  ) ) | float | round(0)
          < 5400 ) %} 
            {% set ns.sum = ns.sum + states('sensor.2f_trv_living2_load_estimate') | float %}
            {% set ns.count = ns.count + 1 %}
          {% endif %}
        {% endif %} {% if (states('sensor.2f_trv_living3_load_estimate') | float
        > -500) %} 
         {% if  ((as_timestamp(now()) 
          - as_timestamp(states.sensor['2f_trv_living3_load_estimate'].last_updated  ) ) | float | round(0)
          < 5400 ) %} 
            {% set ns.sum = ns.sum + states('sensor.2f_trv_living3_load_estimate') | float %}
            {% set ns.count = ns.count + 1 %}
          {% endif %}
        {% endif %} {% if ns.count >0 %} 
          {% set ns.avg = ns.sum/ns.count %}
        {% endif %}  {{ ns.avg | round(0)  }}
    target:
      entity_id:
        - number.2f_trv_living1_load_room_mean
        - number.2f_trv_living2_load_room_mean
        - number.2f_trv_living3_load_room_mean
mode: single

1 Like

Hi all,

For a while I am busy with using Danfoss Ally trv in a covered way with an external temperature sensor. However, it is not able to reach and maintain a setpoint temperature yet.

zha_toolkit and the blueprints there to configure and update external temperature helped a lot. Currently I am using an automation based on danfoss_ally_remote_temperature.yaml with 30mins as max update interval.

However I still see my trv’s falling back to internal temperature sensor occasionally. Here is a snapshot of what is happening:
1st graph is a periodic(5min) read of trv’s external temperature sensor value.
2nd is heating demand.
3rd is actual state of external temperature sensor.

Note that trv is falling back to -8000 value pretty quickly, about 30mins.

I suspect two causes and solutions:

  1. Sometimes zha attr write to trv external temp fails. This can be solved by having ‘tries: X’ argument for zha_toolkit.attr_write.
  2. If the external temp value has not changed then the automation will not write. This can be solved by having ‘write_if_equal: true’ argument for zha_toolkit.attr_write.

I would be glad for any comments.

I am not yet bothered with if the trv is able to achieve and maintain the setpoint temperature since this current problem is a blocker. Next I will try to assess that.

@surreallo The danfoss document links are dead. Can you please provide the name of the document?

@oguzcanoguz
I think it’s the
Feature Catalogue
Ally™ electronic Radiator Thermostat (eTRV)

@le_top

Hello, I have been receiving the same errors regarding my thermostatic heads for some time now. It look like that:
Logger: homeassistant.components.script.1700562031216
Source: helpers/script.py:2005
integration: Script (documentation, issues)
First occurred: 08:39:53 (2 occurrences)
Last logged: 08:48:12

Small room sensor temperature: Error executing script. Error for call_service at pos 1: Failed to set attribute: value: 2730 attribute: 16405 cluster_id: 513 endpoint_id: 1
Small room sensor temperature: Error executing script. Error for call_service at pos 1: Failed to set attribute: value: 2740 attribute: 16405 cluster_id: 513 endpoint_id: 1

and

Logger: homeassistant.components.automation.actualization_temperature_small
Source: helpers/script.py:2005
integration: Automation (documentation, issues)
First occurred: 08:39:53 (2 occurrences)
Last logged: 08:48:12

Actualization temperature Small: Error executing script. Error for call_service at pos 1: Failed to set attribute: value: 2730 attribute: 16405 cluster_id: 513 endpoint_id: 1
Actualization temperature Small: Error executing script. Error for call_service at pos 1: Failed to set attribute: value: 2740 attribute: 16405 cluster_id: 513 endpoint_id: 1

and

Logger: homeassistant.components.automation.actualization_temperature_small
Source: components/automation/init.py:744
integration: Automation (documentation, issues)
First occurred: 08:39:53 (2 occurrences)
Last logged: 08:48:12

Error while executing automation automation.actualization_temperature_small: Failed to set attribute: value: 2730 attribute: 16405 cluster_id: 513 endpoint_id: 1
Error while executing automation automation.actualization_temperature_small: Failed to set attribute: value: 2740 attribute: 16405 cluster_id: 513 endpoint_id: 1

Hey there,

I am receiving similar errors for 1 of my automations. I have in total 3 automations that feed external temp to 3 Danfoss TRV’s. Only one of the TRV’s fails to receive attribute set (totally at random).

Fails randomly both from automation and manual attempts:

service: zha.set_zigbee_cluster_attribute
data:
  cluster_type: in
  ieee: 34:10:f4:ff:fe:62:75:7f
  endpoint_id: 1
  cluster_id: 513
  attribute: 16405
  value: >-
    {{ (states('sensor.zhimi_mb3_853f_temperature') |
    float(state_attr('climate.danfoss_etrv0103_thermostat_2',
    'current_temperature')) * 100) | round(0) }}

2024-06-09 12:00:05.172 ERROR (MainThread) [homeassistant.components.automation.aktualizuj_temperature_termostatu_2] Termostat Gabinet: Aktualizuj temperaturę: Error executing script. Error for call_service at pos 1: Failed to set attribute: value: 2440 attribute: 16405 cluster_id: 513 endpoint_id: 1
2024-06-09 12:00:05.173 ERROR (MainThread) [homeassistant.components.automation.aktualizuj_temperature_termostatu_2] Error while executing automation automation.aktualizuj_temperature_termostatu_2: Failed to set attribute: value: 2440 attribute: 16405 cluster_id: 513 endpoint_id: 1
2024-06-09 12:45:02.911 ERROR (MainThread) [homeassistant.helpers.script.websocket_api_script] websocket_api script: Error executing script. Error for call_service at pos 1: Failed to set attribute: value: 2440 attribute: 16405 cluster_id: 513 endpoint_id: 1
2024-06-09 12:45:02.913 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140632112369376] Error handling message: Failed to set attribute: value: 2440 attribute: 16405 cluster_id: 513 endpoint_id: 1 (home_assistant_error) Patryk from 172.16.16.4 (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0)
2024-06-09 12:48:17.548 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140632112369376] Error handling message: Unknown error (unknown_error) Patryk from 172.16.16.4 (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0)
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/websocket_api/decorators.py", line 27, in _handle_async_response
await func(hass, connection, msg)
File "/usr/src/homeassistant/homeassistant/components/zha/websocket_api.py", line 829, in websocket_read_zigbee_cluster_attributes
success, failure = await cluster.read_attributes(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 528, in read_attributes
result = await self.read_attributes_raw(to_read, manufacturer=manufacturer)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/__init__.py", line 268, in read_attributes_raw
return await super().read_attributes_raw(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zhaquirks/danfoss/thermostat.py", line 223, in _read_attributes
return await self.split_command(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zhaquirks/danfoss/thermostat.py", line 210, in split_command
await func(records_standard, *args, **kwargs) if records_standard else []
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/__init__.py", line 334, in _read_attributes
return await super()._read_attributes(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 377, in request
return await self._endpoint.request(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/endpoint.py", line 265, in request
return await self.device.request(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/device.py", line 339, in request
await send_request()
File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 841, in request
await self.send_packet(
File "/usr/local/lib/python3.12/site-packages/bellows/zigbee/application.py", line 931, in send_packet
raise zigpy.exceptions.DeliveryError(
zigpy.exceptions.DeliveryError: Failed to deliver message: <EmberStatus.DELIVERY_FAILED: 102>
2024-06-09 12:50:35.515 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140632112369376] Unexpected exception
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/zha/core/device.py", line 789, in write_zigbee_attribute
response = await cluster.write_attributes(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zhaquirks/danfoss/thermostat.py", line 363, in write_attributes
write_res = await super().write_attributes(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 626, in write_attributes
return await self.write_attributes_raw(attrs, manufacturer)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 632, in write_attributes_raw
result = await self._write_attributes(attrs, manufacturer=manufacturer)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/quirks/__init__.py", line 350, in _write_attributes
return await super()._write_attributes(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/zcl/__init__.py", line 377, in request
return await self._endpoint.request(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/endpoint.py", line 265, in request
return await self.device.request(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/zigpy/device.py", line 339, in request
await send_request()
File "/usr/local/lib/python3.12/site-packages/zigpy/application.py", line 841, in request
await self.send_packet(
File "/usr/local/lib/python3.12/site-packages/bellows/zigbee/application.py", line 931, in send_packet
raise zigpy.exceptions.DeliveryError(
zigpy.exceptions.DeliveryError: Failed to deliver message: <EmberStatus.DELIVERY_FAILED: 102>
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 241, in handle_call_service
response = await hass.services.async_call(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/core.py", line 2741, in async_call
response_data = await coro
^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/core.py", line 2784, in _execute_service
return await target(service_call)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 1080, in _async_admin_handler
await result
File "/usr/src/homeassistant/homeassistant/components/zha/websocket_api.py", line 1305, in set_zigbee_cluster_attributes
response = await zha_device.write_zigbee_attribute(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/zha/core/device.py", line 793, in write_zigbee_attribute
raise HomeAssistantError(
homeassistant.exceptions.HomeAssistantError: Failed to set attribute: value: 2450 attribute: 16405 cluster_id: 513 endpoint_id: 1

TRV itself seems to work just fine, except of this external data feed problem.

2 remaining TRVs (same model, same firmware) do not report such problems.

Is anyone running the Danfoss Ally with a heat pump and radiators and is happy? I use the TRVs without the Danfoss Gateway but with own coordinator and zigme2mqtt and HA.

I got the Danfoss Ally and the radiator for the first heating season now. While the transitional heating season is not easy to configure in heaters anyway, the Allys seems not to work well with heat pump at all. They close the valves totally when they think they are warm enough just to open them later again. That is a killer for heat pumps, because they get not away the heat and go on and off a lot which is bad for time to live.

I thought the Allys are doing an automatic hydraulic adjustment of the whole system with all radiators for heaving the perfect water flow in the heat system. But it seems the Allys just care about their own room. Opening and closing valves is the opposite of a perfect flow. I wish I could tell them to stay open always a minimum of 30% or something like that to arrange the rest with the configuration in the heat pump.

Is the setting “Automatic adaptation run enabled” needed or can I turn it off once it has found its valve characteristic? :thinking: