Eurotronic Spirit Z-Wave - external temperature sensor

Hi @ExTrEmE

You definitly can, that was the whole purpose for me choosing it. You will need to adjust the Measured Temperature Offset which can be adjusted to -5 C to +5 C of the internal measured temperature. See my previous post about how to adjust this temperature in automation.yaml.

I have recently set this up in the hassio node-red addon and can link that too if you want.

1 Like

Hi,
even though I use ioBroker instead of HomeAssistant Iā€™m fighting the same fight trying to feed the Spirit with an external temperature.
has anybody had any luck with reaching out to Eurotronic?

Thanks,
Kevin

Just as a random note:

I have several Eurotronic Spirit Z-wave and some have a different firmware version so I emailed Eurotronic about it and if there are some files available to update - they replied in a one liner - Yes it can upate itself but they wonā€™t publish any new(?) updates due to several hub vendors not offering that feature.

Hello @Siggylicious please share me Node-red addon. Did you improve you previous code?

@Siggylicious of course this is the easy-way. Set an adjusting offset with parameter and stop (and this is working also with my zipabox).
If I understood, you get the trv temp, other sensor temp, calculate an average temp, and then set the offset to the average-trv difference. right?

@jelennn below is my node-red flow. It could proberly have been done smarter but it is more or less a what I had in my automation.yaml put directly into node-red.

[{"id":"addb4d7c.36da8","type":"server-state-changed","z":"4d2ad910.e13508","name":"TRV temp change","server":"154dc902.7c20a7","version":1,"entityidfilter":"sensor.eurotronic_eur_spiritz_wall_radiator_thermostat_temperature","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":110,"y":340,"wires":[["302d2b0e.b043f4","de11714d.4817f"]]},{"id":"302d2b0e.b043f4","type":"function","z":"4d2ad910.e13508","name":"Real office temp","func":"const globalHomeAssistant = global.get('homeassistant');\n\nmsg.payload = (globalHomeAssistant.homeAssistant.states[\"sensor.eurotronic_eur_spiritz_wall_radiator_thermostat_temperature\"].state - globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_degrees\"].state + 1).toFixed(2);\n\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":400,"wires":[["ae0d7e4e.0b49f"]]},{"id":"de11714d.4817f","type":"function","z":"4d2ad910.e13508","name":"Avg office temp","func":"const globalHomeAssistant = global.get('homeassistant');\n\nmsg.payload = ((Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_trv_real_temp\"].state) + Number(globalHomeAssistant.homeAssistant.states[\"sensor.0x00158d00025d8dfd_temperature\"].state)) / 2).toFixed(2);\n\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":460,"wires":[["1e203e93.77f1b1"]]},{"id":"59b9a73b.3e7308","type":"function","z":"4d2ad910.e13508","name":"Set temp offset in degrees","func":"const globalHomeAssistant = global.get('homeassistant');\n\nmsg.payload = (Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_avg\"].state) - Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_trv_real_temp\"].state)).toFixed(2);\n\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":520,"wires":[["d504b39d.062c9"]]},{"id":"30f2088b.9ff0b8","type":"function","z":"4d2ad910.e13508","name":"Set temp offset to match config param","func":"const globalHomeAssistant = global.get('homeassistant');\n\nif ( Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_degrees\"].state) > 5)\n{\n    msg.payload = 50;\n}\nelse if( Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_degrees\"].state) >= 0)\n{\n    msg.payload = (Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_degrees\"].state) * 10).toFixed(0);\n}\nelse if( Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_degrees\"].state) < -5)\n{\n    msg.payload = 206;\n}\nelse\n{\n    msg.payload = 256 + (Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_degrees\"].state) * 10).toFixed(0);\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":580,"wires":[["b365558f.414cd8"]]},{"id":"ae0d7e4e.0b49f","type":"api-call-service","z":"4d2ad910.e13508","name":"Set payload","server":"154dc902.7c20a7","version":1,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.office_trv_real_temp","data":"{\"value\":{{payload}}}","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":670,"y":400,"wires":[[]]},{"id":"1e203e93.77f1b1","type":"api-call-service","z":"4d2ad910.e13508","name":"Set payload","server":"154dc902.7c20a7","version":1,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.office_temp_avg","data":"{\"value\":{{payload}}}","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":670,"y":460,"wires":[[]]},{"id":"a71abbaa.9f41c8","type":"server-state-changed","z":"4d2ad910.e13508","name":"Office temp change","server":"154dc902.7c20a7","version":1,"entityidfilter":"sensor.0x00158d00025d8dfd_temperature","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":110,"y":420,"wires":[["de11714d.4817f"]]},{"id":"d504b39d.062c9","type":"api-call-service","z":"4d2ad910.e13508","name":"Set payload","server":"154dc902.7c20a7","version":1,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.office_temp_offset_in_degrees","data":"{\"value\":{{payload}}}","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":670,"y":520,"wires":[[]]},{"id":"e6a71036.5de0b","type":"server-state-changed","z":"4d2ad910.e13508","name":"TRV real temp change","server":"154dc902.7c20a7","version":1,"entityidfilter":"input_number.office_trv_real_temp","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":120,"y":500,"wires":[["59b9a73b.3e7308"]]},{"id":"14aa7c5d.b0bdb4","type":"server-state-changed","z":"4d2ad910.e13508","name":"Office temp offset change","server":"154dc902.7c20a7","version":1,"entityidfilter":"input_number.office_temp_offset_in_degrees","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":130,"y":580,"wires":[["30f2088b.9ff0b8"]]},{"id":"b365558f.414cd8","type":"api-call-service","z":"4d2ad910.e13508","name":"Set payload","server":"154dc902.7c20a7","version":1,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.office_temp_offset_in_byte","data":"{\"value\":{{payload}}}","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":710,"y":580,"wires":[[]]},{"id":"738aadb.35eda54","type":"server-state-changed","z":"4d2ad910.e13508","name":"Send temp offset to TRV","server":"154dc902.7c20a7","version":1,"entityidfilter":"input_number.office_temp_offset_in_byte","entityidfiltertype":"exact","outputinitially":true,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"x":130,"y":660,"wires":[["2f7432d3.506e9e"]]},{"id":"21c71b76.4f65a4","type":"api-call-service","z":"4d2ad910.e13508","name":"Set payload","server":"154dc902.7c20a7","version":1,"debugenabled":false,"service_domain":"zwave","service":"set_config_parameter","entityId":"","data":"{\"node_id\": 9, \"parameter\": 8, \"value\":{{payload}}}","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":730,"y":660,"wires":[[]]},{"id":"2f7432d3.506e9e","type":"function","z":"4d2ad910.e13508","name":"Set temp offset to match config param","func":"const globalHomeAssistant = global.get('homeassistant');\n\nmsg.payload = Number(globalHomeAssistant.homeAssistant.states[\"input_number.office_temp_offset_in_byte\"].state).toFixed(0);\n\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":660,"wires":[["21c71b76.4f65a4"]]},{"id":"154dc902.7c20a7","type":"server","z":"","name":"Home Assistant"}]

@ExTrEmE yup. Get the offset and convert it and then send it.

Hello,

Iam using a Zipato Zipabox for that work. Create a virtual thermostat. This device will have several in- and outputs. Outputs are set setpoints for the Spirit. Inputs are measred temperatures from any device which is able to work like a sensor. If you use a SRT 321 device you are able to change the set temp without using a App or other interface. One virt. Thermostat can controll several Spirits. I am using it for a big house with several configurations. For your convinience you can plan for every item night/day temp.settings.
Tipp: You can use for all this a Zipabox 1 which is very cheap to buy. Because Zipato has a working HTTP interface you can link it to any system which talks HTTP. I use it for 4 years and saved per year all my investments by reducing costs for gas!

Itā€™s correct. Virtual thermostat is able to change setpoint.
Unfortunately with zipabox I was unable to se the valve to read the temp from another sensor; config param set to the right value, another sensors associated with the valveā€¦ nothing: probably the issue is not entirely a zipabox fault.

I got it finally working with an external temperature sensor. the eurotronic thermostats are a bit picky about the sensors they work with. The only ones I found are the sensors from eurotronic itself (https://eurotronic.org/produkte/sensoren/). You simply have to set the thermostat option Measured Temperature Offset to 128 and put the thermostat into the ā€œtemperatureā€ association group of the sensor.

homeassistant does not support the sensors out of the box yet. I have committed the necessary changes to homeassistant today so I hope the sensors will be supported soon.

P.S: For those wondering where to buy these sensors, I could only find them at https://www.hornbach.de/shop/suche/sortiment/eurotronic.

2 Likes

I would like to provide my experience with Aeotec radiator thermostat (ZWA021) and Home Assistant, as I did not find any information about this anywhere prior to my purchase, nor is it documented in the product manual.

The thermostat works fine when using the internal temperature probe. However, when setting parameter 8 to 128 (use external sensor) and associating it with an Aeotec Multisensor 6, the thermostat no longer works as expected. For example, if you set the thermostat to OFF, it will automatically open the valve after 10-60 seconds (probably when it gets a temperature report from Multisensor). The thermostat will still show OFF on the LCD screen, however the valve will be opened.

I have contacted Aeotec on this issue and their reply was that:

You are trying to use Motion Sensor 6 to send a temperature to the thermostat by association.
Unfortunately, the thermostat is not able to evaluate the values from the Lifeline, which contains much more data than just the temperature.
In this case the value is recognized as 0 and the thermostat tries to reach the set temperature.
It is not possible to use it in this way.
You need an external temperature sensor, which only sends the temperature via a separate association group.
A current example would be a Secure SRT321.
In Q2 we will also have a sensor that is capable of this. Our aerQ sensor has the required association group.

So, for anyone looking to buy Aeotec ZWA021 thermostat and hoping that it will work out of the box with existing Aeotec Multisensor 6ā€™s, you can forget about it. For now, it seems that the only way to make this thermostat smart is to create automation rules in Home Assistant (e.g. set thermostat temperature/valve to Max, when Multisensor 6 reaches desired temperature, set thermostat to OFF)

I am very disappointed in Aeotecā€™s practices and have already complained to them that not providing compatibility between Aeotecā€™s own products is bad business practice and misleading to the consumer.

After some more tinkering I managed to reach an usable setup, which relies on the logic of generic_thermostat, rather than Aeotecā€™s implementation. This setup also uses an external temperature sensor to control the thermostat.

Thanks to @insajd for the tip: https://github.com/home-assistant/core/issues/14526#issuecomment-535944520

1. Z-wave settings
In Z-wave config panel, set parameter 5 of the thermostat (Temp Report Threshold) to 5 or whatever value you desire. Set parameter 6 to 0 as we wonā€™t be using valve percentage reports. Set parameter 8 to 0, as we wonā€™t be using offsets anymore. You donā€™t need to set parameter 8 to 128, as the logic will be handled by HA, not by the thermostat itself.

From the thermostat card, set thermostat preset_mode to Manufacturer Specific to allow direct valve level control.

2. Z-wave custom component

Go to https://github.com/home-assistant/home-assistant
Click download.
Extract the archive.
Open folder home-assistant-dev\homeassistant\components\zwave
Copy this folder(zwave) into your custom_components folder (in my case /docker/homeassistant/custom_components/zwave )
Edit discovery_schemas.py according to the changes made here:
https://gist.github.com/insajd/b1c922284ec2e38e84704db16429e001/revisions

Basically, add lines 87-91, 104, 111 to discovery_schemas.py in their respective locations.

If you restart HA at this point youā€™ll have a new switch something like ā€œcover.aeotec_limited_zwa021_thermostatic_valve_levelā€

3. Convert linear switch to on/off
The cover switch allows changes from 0-100%, representing the thermostat valve open level. In order to use it in a thermostat, we need to change it to an on/off switch. For this, add the following in your configuration.yaml:

 switch:
   - platform: template
     switches:
       termostat_living:
         value_template: "{{ is_state('cover.aeotec_limited_zwa021_thermostatic_valve_level', 'open') }}"
         turn_on:
           service: cover.set_cover_position
           data:
             entity_id: cover.aeotec_limited_zwa021_thermostatic_valve_level
             position: 100
         turn_off:
           service: cover.set_cover_position
           data:
             entity_id: cover.aeotec_limited_zwa021_thermostatic_valve_level
             position: 0

4. Set-up generic_thermostat

Add the following in your configuration.yaml:

 climate:
   - platform: generic_thermostat
     name: Termostat Living
     heater: switch.termostat_living
     target_sensor: sensor.aeon_labs_zw100_multisensor_6_temperature

target_sensor: is the external sensor you want to use to control the room temperature. I have used the output of an Aeotec Multisensor 6 here.

5. Lovelace

Add a thermostat card to lovelace using switch.termostat_living as entity (created by generic_thermostat).

6. Restart HA

You should now have a thermostat card which will turn the valve to 100% when the temperature is lower than the setpoint and will close the valve when the desired temperature is reached.

2 Likes

more expensive than the valve itself :open_mouth: :open_mouth:

1 Like

I thought I would share my automation here for these valves with the new zwavejs integration, which I am basing off @insajdā€™s code. Please note that currently there is a problem running this with the zwave2mqtt add-on (v 0.6.1) ā€“ you have to use the integrated zwavejs integration, but then you cannot actually check if the parameters are being set correctly.

- id: auto_trv_salon_correction
  alias: "Heating: Correct TRV Salon Temp"
  description: Correct TRV Salon Temperature to that given by room Xiaomi thermometer
  trigger:
    - platform: state
      entity_id: sensor.trv_salon_air_temperature
    - platform: state
      entity_id: sensor.xiaomi_salon_temperature
  condition: []
  action:
    - service: input_number.set_value
      target:
        entity_id: input_number.salon_temperature_correction
      data:
        value: >
          {## Read room temp from sensor and from TRV. Calculate difference ##}
          {% set sensor_temp = float(states('sensor.xiaomi_salon_temperature')) %}
          {% set trv_temp = float(states('sensor.trv_salon_air_temperature')) %}
           {% set corr = [-5,(sensor_temp - trv_temp + old_correction)]|max %} 
          {% set corr= [5,corr]|min %} 
          {{ '%0.1f'|format(corr) }}

    - service: zwave_js.set_config_parameter
      data:
        parameter: "8"
        value: >
          {## This takes the above temp corr, multiplies by 10 and passes ##}
          {## to the config param ##}   
          {% set corr = 10*float(states('input_number.salon_temperature_correction'))  %}  
          {{'%0.0f'|format(corr)}}
      target:
        device_id: 32ab6dc7b858f9a872df75b8d891bef7
  mode: restart

trv_salon is my Spirit and xiaomi_salon_temperature is the bluetooth temp sensor in the middle of the room. I then have a number input set up in the helpers input_number.salon_temperature_correction, which tracks the correction which has been written intot he valve. The problem is that there is no way of reading it back, so if something gets out of sync, the correction rapidly increases to +/-5 and you have to fix it. It hasnā€™t happened here since I was setting it up, but YMMV.

Itā€™s important that when you run this automation for the first time, you zero both the temp offset parameter (no. 8) in the valve and the input_number or they will start off out of syncā€¦

Notice that zwavejs accepts negative integers and does the conversion inside, so you donā€™t have to go through the shenanigans of subtracting them from 256.

Anyway, the changes are just cosmetic.

Iā€™ve seen a lot of posts slamming these thermostats and I have something to addā€¦ my house is strange, some rooms are natural furnaces and others more like a refrigerator. Iā€™ve noticed a behaviour in the thermostats where the warmer room the thermostat is ā€œsoftā€ valve opens less and in the cooler room the valve opens more even when the target temp is reachedā€¦ personally I think these things learn if a room is a heat dump or a furnace and behave accordinglyā€¦ pretty smart really

  1. Set parameter 8 to 0x80 (listen for external sensor)
  2. Deliver ambient temperature to Spirit
    There is no way to call any HA service, but you can send direct Z-Wave data, using low-level API

Comand string format ā€œ0x31 0x05 0x01 0x42 0x%02x 0x%02xā€
Last 2 parameters - 2byte of ambient temperature. Hi and low.

Recently I installed 2 of these TRVs for test, with potential to install another 9ā€¦ I implemented for them very similar approach as you did (same logic, different templating). But I run into few issues:

  • I noticed that for some reason parameter 8 actual value written to thermostat (and then showing in configuration as value of this parameter) is not the one that is calculated in template. It is always off-set by -1 (for positive values) or by 1 (for negative values). E.g. if I call service with parameter 8 value of -17 actual value that is set is -16. This is causing that input_number used to store assumed state of this parameter is gradually drifting from actual value, making difference between external sensor and what TRV is reporting and using to regulate temperature. I overcomed this by appropriate modification of templateā€¦ yet need to see how it works in practice.
  • with one TRV I get to situation, when calculated correction is exceeding Ā± 50, so it cannot be corrected. It is ridiculously high difference - externally measure temperature is 22.0 C, while internal TRV sensor shows 29.8 C. So not only it cannot be corrected, but also, given the fact that the valve is completely closed and cold it raises the question if TRV is somehow broken? It developed such difference over time, initially corrections for this TRV were within 1~2 C, which seems reasonable.
    Did you, or anyone else run into such problems?

I have to say that Iā€™ve had this automation work fine since I wrote it last winter. It was off in the summer, but it has been back on since September and I donā€™t see any issues.

This is interesting. Could you let us know how to write such low-level commends using ZWaveJS?

There are no Spirit installations with HA/ZJS, i cannot write or test anything.
Only installations with Vera are exists.

But you can oversee ZJS documentation here:
https://zwave-js.github.io/node-zwave-js/#/usage/custom
Actually Spirit waiting for incoming SENSOR_MULTILEVEL_REPORT, with precession 2.
If you t. sensor can send report to associated devices, you can link it at Z-Wave layer and do nothing in high-level logic.

I was checking zwave specification and found a message structure:


so in our case 15.12 C report to the target node should look like:

Multilevel Sensor 0x31
Command = SENSOR_MULTILEVEL_REPORT 0x03
Sensor Type(air temp) 0x01
precision  0x02
Scale (celsius) 0x00
size (2 bytes) 0x02  #not sure about size
value 0x0E 0x0C or 0x44bd0000 #not sure how to convert temperature to the right value

I couldnā€™t find how to feed this line to OZWaveJS