Danfoss Ally TRV working with remote temp sensor

+1 here, definitely interested in this card :slight_smile:

+1 also interested…

Can you give me the card code?

Hi. Will this work with deconz? Or just zigbee2mqtt?

It says I have the version 01.12.0008 01.12 is that correct?

I did a pretty simple appdaemon app that controls Ally TRVs through zha, including the load balancing. Code is on github along with the config. There’s also a small module that periodically syncs an external temperature sensor to the TRVs.

I ran into an issue where the load_estimate was negative, but the zha quirk for Danfoss thinks it’s an unsigned int. To fix that I created a small custom quirk (which I put in /config/custom_quirks/__init__.py and then set zha/custom_quirks_path in configuration.yaml):

from zhaquirks.danfoss import DanfossThermostatCluster
import zigpy.types as t


DanfossThermostatCluster.manufacturer_attributes[0x4040] = ("load_radiator_room_mean", t.int16_t)

Now, I don’t know if all of this actually has the intended effect, but at least it seems to be doing the right thing :slight_smile:

1 Like

Hi Guys

Recently got an ally and got it integrated in z2m and running in covered mode with external temp sensor. This ally is on the kitchen radiator and the thing is, the radiator is really poorly placed. it is basically behind the counters with a open port over it.
When the room load is more or less constant the ally is able to track the temperature setpoint pretty but it over/undershoots quite a lot and you can also see on the valve opening (picture)

what are you guys experience with the Adaptation run control, as I understand it, the ally will try to “learn” the room and thereby better know how to control the temperature

I’ve seen in other comments that in ZHA people have enabled Adaptation run control by sending commands to the zigbee cluster ID, is there an equivalent way to do this in z2m and do you guys believe it will help ?

Control algorithm scale factor = 5

Control algorithm scale factor = 3

temp response with scale factor = 3 and setpoint = 21

+1 I’m also very interested of this card :slight_smile:

If anyone else has an example how to create a sensor for Danfoss Ally valve state (pi_heating_demand I guess) I would be interested :slight_smile:

1 Like

I was able to figure out myself how to get Danfoss Ally valve state by reading the PIHeatingDemand attribute periodically. I have updated the firmware of my Allys to version 1.18, haven’t tested this on earlier firmware versions. Here’s how I did it, hopefully someone else might find this useful:

First I installed zha-toolkit custom component: GitHub - mdeweerd/zha-toolkit: 🧰 Zigbee Home Assistant Toolkit - service for "rare" Zigbee operations using ZHA on Home Assistant. This custom components allows (among other things) one to read Zigbee cluster attribute and write it to a state of a sensor: GitHub - mdeweerd/zha-toolkit: 🧰 Zigbee Home Assistant Toolkit - service for "rare" Zigbee operations using ZHA on Home Assistant.

So as first step I created an automation that reads the PIHeatingDemand of my Danfoss Ally TRVs to sensors. Below is an example that creates a sensor called sensor.office_radiator_thermostat_valve_raw and writes PIHeatingDemand to it every 5 minutes

id: read_thermostat_values_to_sensors
alias: Read additional thermostat values and store them to sensors
trigger:
 - platform: time_pattern
   minutes: /5
condition: []
action:
 # Office radiator valve state
 - service: zha_toolkit.execute
   data:
     command: attr_read
     ieee: paste_your_trv_ieee_here
     endpoint: 1
     cluster: 0x0201
     attribute: 0x0008
     state_id: sensor.office_radiator_thermostat_valve_raw
     allow_create: True

Then I created a template sensor that follows the raw value and has a proper measurement unit and an icon set (I tried first customization for the raw sensor, but that didn’t work for some reason):

sensor:
  - name: "Office radiator thermostat valve"
    unique_id: office_thermostat_valve_wrapper
    unit_of_measurement: "%"
    icon: mdi:valve
    state: >
      {{ states('sensor.office_radiator_thermostat_valve_raw') | int(0) }}

This is jut one example how to create sensors from Ally TRV attributes. You can easily follow pretty much any attribute just by changing the cluster and attribute values of zha_toolkit.execute service call.

1 Like

Happy to see that this feature is used !

I think there is no need to create a separate sensor if no value conversion or default value is needed.
The sensor state can be updated directly from the zha_toolkit.

Also note that instead of the IEEE address you can provide the entity name, and if there’s only one cluster of type 0x0201 in the device you can even omit the endpoint (but when setting it, zha_toolkit does not have to spend time and energy on finding these values).

I just added a template sensor with:

- platform: template
  sensors:
    livingroom_heating:
      friendly_name: "Olohuone termostaatti ohjaus"
      unit_of_measurement: '%'
      value_template: "{{ state_attr('climate.danfoss_etrv0100_274b5ffe_thermostat', 'pi_heating_demand')}}"

Didn’t need the ZHA-toolkit

4 Likes

Interesting, I didn’t know that Zigbee cluster attributes are available as state attributes of the Ally thermostat entity. How often do the attributes update? If the update interval is acceptable this looks like a very elegant way to get the valve state :+1:

The fastest seems to be about every 7-10min.

And you could configure that using zha-toolkit/conf_report !

+1 If possible I would also ask for instructions on how to do it.
Thank you in advance.

Hi all, first post in this community.

I’ve recently bought these TRVs and installed Home Assistant, still learning home assistant concepts etc. This thread (and a few others) has great amount of info to learn from regarding these TRVs, appreciate the discussions here.

I noticed there is a relatively recent “Programming Guide” document (June 08, 2022) on the official Danfoss support site. Not sure if it was unavailable before, or if it is just a new version, but it has a wealth of information about this product and actually explains some of the odd features that have been discussed here. I’ll outline the main points here, but I encourage everyone to go to the official support site and read it themselves: Danfoss Ally™ support | Danfoss

External Temperature Sensor
There were some guesses in this thread of how the behavior of this TRV is when using an external sensor, specifically in (default) uncovered mode. The guide explains that the eTRV actually still uses it’s internal temperature sensor for operation, but the reported external room temperature is used to set a temperature offset between the internally measured and actual room temperature, and is not measuring by an average between them. This explains why the initial guidelines in this mode were to report this value once every few hours, but not more often than 30 minutes - the offset should not change so often, and should be relatively constant with normal operation. I would imagine this would also save battery life due to less frequent communication. Additionally, it explains that the max offset is ±4 degrees, which also explains why some failed to see big differences in operation, even after putting the external sensor outside.
For covered mode it acts as was already discussed here - the internal measurement is ignored, and that is also why this mode requires more frequent communication.

Alternatively, it seems one could just set a raw offset value.

Load Balancing
They explain how the room load balancing is supposed to work:

The gateway calculates an average of the load for all the
radiators in the room and distributes it to all the eTRVs in the
room via the attribute 0x4040 ”Load Radiator Room Mean”
every 15 minutes.
The Gateway must discard all the values below -500 (too
low) down to -8000 (invalid/inactive) and values older than
90 minutes. The average must then be calculated with the
values from the other eTRVs in the room.

Sending the -8000 value will actually deactivate the feature

Another interesting thing I noticed - there is a manufacturer specific command which can be used to set the heating temperature with different behaviors. The standard approach of just setting the setpoint (which I assume is what happens by default) will, as I understand, gradually change the behavior. However, there is also a “user interaction” setpoint change, which is equivalent to the user physically turning the TRV knob, and will do an aggressive adjustment to arrive at the requested temperature as soon as possible.

During a User Interaction (setpoint change by turning the dial or setpoint change via ”SetpointCommand + UI=1”)
the motor jumps to a much different position regardless of limitations for battery saving and energy
efficiency.
NOTE: The aggressive command (type 1) must not be used all the time for schedule changes!!! If done, it
could reduce comfort, energy efficiency and battery lifetime!

It seems the main intended use case is to pick up when the dial was turned on one TRV, and then this command can be sent to other TRVs in the room, to force them also to do an “aggressive” adjustment, or use it when a user changes the temperature in dashboard.

The document also has nice pictures and operation/communication examples, and also describes other features like display orientation, installation orientation, eTRV internal schedules etc.

I’m still just starting out with Home Assistant, so many home-assistant / zigbee details are a bit over my head for now, but so far most or all of these features seem relatively straightforward to implement.

4 Likes

Interesting stuff! Thank you for the deep dive!

I tried my hand at creating an automation blueprint for updating the external temperature for this TRV. This automation is meant to respect the Danfoss recommended intervals (at least every X, but not more often than Y), though these values have to be configured manually.

It uses the external sensor state change as the primary trigger, and additionally uses a timer to refresh the temperature after the required period, even if the temperature sensor did not change during this time. You have to manually create a timer for each TRV and set the timer interval accordingly - for uncovered mode it should be “at least every 3 hours” (I use 2h30m), and for covered it should be “at least every 30 minutes” because the covered mode will switch off if no reading is received for 35 minutes. I think it is safe to use max values for each timer as it is only a fallback. You should create a new timer for each TRV as it will be reset on each automation activation, so reusing one timer might cause some of the TRVs to go without a reading for a longer time (though technically I think it should be OK to reuse timer if you also use the same source temperature sensor, but I’d still create unique ones to be sure)

For the “Minimum update interval” input you should again use corresponding value depending on if the TRV is in covered or uncovered mode. By Danfoss documentation it should be 30 min for uncovered and 5min for covered.

Here is the blueprint you can put in your /config/blueprints/automation/ or a subdirectory within.

blueprint:
  domain: automation
  name: Ally Temp Update
  description: Update Danfoss Ally TRV external temperature with min/max refresh rate
  input:
    ally_device:
      name: Ally TRV Device
      description: Temperature reading will be sent to this device
      selector:
        device:
          manufacturer: Danfoss
          entity:
            domain: climate
    min_update_minutes:
      name: Minimum update interval
      description: >
        Updates will not be sent if time from last update is less than minimum interval.
        Normally 30min for uncovered, 5min for covered.
      selector:
        number:
          max: 360
          min: 1
          unit_of_measurement: minutes
          mode: box
    temp_sensor_id:
      name: Temperature Sensor
      description: External sensor from which the temperature will be read. Expects data format 12.3
      selector:
        entity:
          domain: sensor
          device_class: temperature
    max_update_timer_id:
      name: Timer entity
      description: >
        Timer that will be (re)started on update.
        Set this timer to slowest interval you want the device to update at.
        Normally 3h for uncovered, 30m for covered.
        Use separate timer for each automation.
      selector:
        entity:
          domain: timer

variables:
  device: !input ally_device
  ieee: "{{(device_attr(device, 'identifiers')|list)[0][1]}}"
  min_update_minutes: !input min_update_minutes
  temp_sensor_id: !input temp_sensor_id
trigger:
- platform: state
  entity_id:
  - !input temp_sensor_id
- platform: event
  event_type: timer.finished
  event_data:
    entity_id: !input max_update_timer_id
condition:
- condition: template
  value_template: >
    {{ as_timestamp(now()) - as_timestamp(state_attr(this.entity_id,'last_triggered'),0)|int
    > (60 * min_update_minutes) }}
action:
- service: zha.set_zigbee_cluster_attribute
  data:
    ieee: '{{ ieee }}'
    endpoint_id: 1
    cluster_id: 513
    cluster_type: in
    attribute: 16405
    value: '{{ (states(temp_sensor_id) | float * 100) | round(0)}}'
- service: timer.start
  target:
    entity_id: !input max_update_timer_id
mode: single

I only tested it briefly so there could be some bugs, but I think it works. Do let me know if someone else tries this if it works for you or not.

edit: had to add default timestamp value, otherwise would fail to run the first time

Hey, nice work, thank you!

Did just install the blueprint and it did not throw an error. Seems like its working, but I don’t know how to test if it really works.
My Card says the thermostat is heating although the temperature is way higher than heating trigger?

Also the temperature the card shows is not the temperature of the external sensor
Which is about 22,7 °C.
As I understood, the external sensor temperature is used to control the heating of the thermostat.

Do you have a hint, where exactly I can see, if this is the case?

Kind regards,
Michael

Yes, the default card will always show the temperature reading from the TRV itself. You can check if the attribute was sent to the TRV by going to the device in settings, and selecting “Manage Clusters” on the device info 3-dot menu. There you should select the correct cluster and attribute (see below) and click “get zigbee attribute”:


The value should be your last sent external sensor reading (i.e. 2270). Worth remembering that the behavior depends on whether your TRV is in “uncovered” (default) mode, or “covered” mode. In uncovered, it calculates an offset from the external reading, but still actually uses the internal sensor. The offset is calculated automatically and limited to ±4 degrees. Whereas in covered mode the internal sensor is ignored, so the external measurement updates are required more often.
At least that’s what Danfoss documentation says.

Judging by other people experiences in this and other threads, it can take a few days for the TRV to “learn” the heating response of the radiator, but it tends to behave well after that. I’m not sure about the state “heating” - we are still not in heating season, so I am just testing my TRVs without actual heating operation, but I also have noticed them being in “heating” mode even if the temperature is higher than the setpoint. I’m not sure how reliable that indicator is.

You can also check the attribute “pi_heating_demand” of the TRV climate entity, which should correspond with the actual open/closed level of the valve. If the measured temperature is higher than setpoint, it should slowly lower.

One last thing to note - I think by default, setting the setpoint through HA or from TRV handle, will initiate the setpoint change command with “user action” command, which will make the TRV to update the setpoint very aggressively, and it can overshoot the target temp. Also something to keep in mind, if you did recently change the setpoint.