Eurotronic Spirit Z-Wave - external temperature sensor

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

I have some plans about migration of installations with Spirit, but not now.
Where you can put a look?
It’s a my template for this task (sorry, my template, for me, i don’t want to export flow for some reasons)

NodeRED used for authomatization, HomeAssistant as device holder and dashboard constructor, Zwavejs2mqtt - as Z-Wave server (For HA - it a ZwaveJS), with node-red-contrib-home-assistant-websocket (set of nodes to interop with HA)

  1. Manual. zwave-js - Z-Wave driver written entirely in JavaScript/TypeScript
    Looks like prec. and length calculated by Zwavejs2mqtt

  2. Simplest way to send anything to anything - MQTT
    How-to:


    Where blue nodes - you HA sensors. No matter, Zigbee, WiFi, or virtual.
    Hexagon - can be tunet to activate, when value changed, (i) - get value
    Gray node - timer
    Green - debug output
    Orange node - format you request. function.
    can be:

var nodeid=100500; // place actual id here
var sensortype=1;
var scale=2;
var value=msg.payload; // data from you sensor. if precesion not 2, you can reformat it


msg.payload={"args":[{"nodeId":nodeid,"commandClass":0x31,"endpoint":0},"set",[sensortype,scale,value]]};

return msg;

purple - MQTT output.
Topic is:

<YOU PREFIX FROM SETUP>/_CLIENTS/ZWAVE_GATEWAY-<NAME FROM SETUP>/api/sendCommand/set

default prefix - zwave, default name - Zwavejs2Mqtt
If ou want to chec output - use MQTT input with same topic, without “set”.
You can use some kind of MQTT explorers, to checkout what happened in real time.
If dry run Ok, remove debug nodes and connect formatted output to MQTT “send”.
and relax :grinning:

It can be constructed in 5 min.
And it is not an ONLY way.
You can use MQTT integration for HA and manipulate topics directly, from HA, tou can use other API.

1 Like

Thanks, I feel like I’m almost there, but not yet.
In the topic, last command is not set, but sendReport. I had every update in zwave2mqtt logged with error when it was set.

<YOU PREFIX FROM SETUP>/_CLIENTS/ZWAVE_GATEWAY-<NAME FROM SETUP>/api/sendCommand/sendReport

What I’m getting now is

2021-11-21 18:14:57.607 INFO MQTT: Message received on zwave/_CLIENTS/ZWAVE_GATEWAY-Mosquitto/api/sendCommand/sendReport: '{"args":[{"nodeId":3,"commandClass":49,"endpoint":0},"set",[1,2,22.5]]}'

And it clearly doesn’t work. Temperature reported still internal, and when external is 23C, internal 18C, setpoint to 22C - valve opens to 80%, so it still uses internal temperature for adjusting.
I tried sending 22.5 as string with 2 decimals, and still same issue.

2021-11-21 18:36:17.597 INFO MQTT: Message received on zwave/_CLIENTS/ZWAVE_GATEWAY-Mosquitto/api/sendCommand/sendReport: '{"args":[{"nodeId":3,"commandClass":49,"endpoint":0},"set",[1,2,"22.80"]]}'

NodeId is 3 because other commands on this TRV mention this number

2021-11-21 18:37:47.412 INFO ZWAVE: Node 3: value updated: 67-0-setpoint-1 22 => 22

Do you have any ideas what could I check or what I am missing?

Update 1:
Now I saw that it might be /set though… so with topic /set I have the following in the logs:


2021-11-21 18:45:39.337 INFO MQTT: Message received on zwave/_CLIENTS/ZWAVE_GATEWAY-Mosquitto/api/sendCommand/set: '{"args":[{"nodeId":3,"commandClass":49,"endpoint":0},"set",[1,2,"22.80"]]}'
2021-11-21 18:45:39.342 INFO ZWAVE: Calling api sendCommand with args: [
{ nodeId: 3, commandClass: 49, endpoint: 0 },
'set',
[ 1, 2, '22.80', [length]: 3 ],
[length]: 3
]
2021-11-21 18:45:39.346 INFO ZWAVE: The command set does not exist for CC 49 sendCommand undefined
2021-11-21 18:45:39.354 INFO MQTT: Message received on zwave/_CLIENTS/ZWAVE_GATEWAY-Mosquitto/api/sendCommand: '{"success":false,"message":"The command set does not exist for CC 49","args":[{"nodeId":3,"commandClass":49,"endpoint":0},"set",[1,2,"22.80"]]}'

Update 2:
So, topic was correct, typo was in the function, in there set should have been changed to sendReport.

2021-11-21 18:49:34.982 INFO MQTT: Message received on zwave/_CLIENTS/ZWAVE_GATEWAY-Mosquitto/api/sendCommand/set: '{"args":[{"nodeId":3,"commandClass":49,"endpoint":0},"sendReport",[1,2,22.8]]}'
2021-11-21 18:49:34.987 INFO ZWAVE: Calling api sendCommand with args: [
{ nodeId: 3, commandClass: 49, endpoint: 0 },
'sendReport',
[ 1, 2, 22.8, [length]: 3 ],
[length]: 3
]
2021-11-21 18:49:36.280 INFO ZWAVE: Success zwave api call sendCommand undefined
2021-11-21 18:49:36.289 INFO MQTT: Message received on zwave/_CLIENTS/ZWAVE_GATEWAY-Mosquitto/api/sendCommand: '{"success":true,"message":"Success zwave api call","args":[{"nodeId":3,"commandClass":49,"endpoint":0},"sendReport",[1,2,22.8]]}'

This is awesome! Thank you for your help.
Now, TRV does report back me the air ambient temperature, but it uses the external temperature that I send it.
TRV 18C, external temp that I send is 22.8, setpoint to 21, and TRV is completely closed. Perfect.

To sum up:
The only thing that needs to be changed is function in nodered, item in the payload args named “set” should be changed to “sendReport”

msg.payload={"args":[{"nodeId":nodeid,"commandClass":0x31,"endpoint":0},"sendReport",[sensortype,scale,value]]};

First of all:
Manual here:

Server awaiting only specific topic. For basic calls - “…/api/sendCommand/set”

About reporting temperature:
My Spirit’s reports ONLY internal sensor data, but works well.
How to test:
Set setpoint to 20C (for example) and put to Spirit fake data - 10C (it must open) and 30C (close)

P.S. Currently migration of 1st installation with Spirit from Vera to HA/Zw2mqtt/NodeRed planned within a 2 month. If you can wait, i can put here some kind of a working solution.

1 Like

Hey @Tmorfus , it’s all good - all working as expected now! I was wrong with topic, the issue was in the payload.
The only thing that was off was item in the payload args named “set” which had to be changed to “sendReport”. I made 2 updates to previous comment while looking for the solution.

msg.payload={"args":[{"nodeId":nodeid,"commandClass":0x31,"endpoint":0},"sendReport",[sensortype,scale,value]]};

It’s all working perfectly for a few hours already, many thanks for detailed instructions!

Thank you @insajd , @Tmorfus ,
I was waiting for it for 3 years :slight_smile:
Now it works fine.
I use home assistant MQTT service:

alias: Bedroom sensor
description: ''
trigger:
  - platform: state
    entity_id: sensor.home_temperature
condition: []
action:
  - service: mqtt.publish
    data:
      topic: zwave/_CLIENTS/ZWAVE_GATEWAY-zwavejs2mqtt/api/sendCommand/set
      payload: >-
        { "args": [ { "nodeId": 6, "commandClass": 49, "endpoint": 0 },
        "sendReport", [ "01", "02",
        "{{states('sensor.home_temperature')}}" ]]}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.bedroom_temp
      value: '{{states(''sensor.home_temperature'')}}'
mode: single

So simple :slight_smile:

3 Likes

There is another one point:
Keep it in “Heating” mode. This device can heat and… nothing morre, becaise it’s a valve :slight_smile:

Some devices, like Eurotronic Air quality monitor, can send ambient temperature data directly to Spirit, only fill Z-Wave associations.

Удачи!

Does anyone know whether this can be made to work through zwave_js_set_value? It seems like it should work but I don’t know where to put sendReport`.

I don’t know, is it possible, or not.
Perhaps possible.
Value API and CC API, are different API’s

Put a look here:
https://zwave-js.github.io/node-zwave-js/#/api/overview
https://zwave-js.github.io/node-zwave-js/#/api/valueid

Try to use notification with Miltilevel CC metadata.

I guess this conversation implies that as of Oct 2021 there is no support in HA to send custom messages over zwave_js (only to configured params), so doing it via mqtt seems like the only way, for now at least.

Support Leviton scene/zone controllers · Issue #2397 · zwave-js/node-zwave-js (github.com)

Pity. I will think about MQTT at some point.