Need help with value_template for MQTT HVAC

I need a template to convert the incoming values (integer) into something meaningful for MQTT HVAC (string). It seems so simple yet I’m not succeeding.

The HVAC system’s operating mode is published as an integer value:
0=auto
1=heat
2=cool
4=off

FanMode is also published as an integer value:
0=auto
2=on

Both topics are published with retain=true so Home Assistant is assured of acquiring it upon startup. Nevertheless, it always reports the system is Off and the fan mode has no value. The only thing that displays and works correctly is the Target temperature.

Here’s what’s in configuration.yaml. The supplied list of modes (auto, heat, cool, off) do appear as selection options for Operation (a.k.a. “mode”). Using MQTTFX, I’ve confirmed the mode topic’s values is currently 1 (“Heat”) yet the UI always displays “Off”. Similarly, Fan Mode’s value is 0 so the UI should say Auto but it actually says nothing at all.

climate:
  - platform: mqtt
    name: "Thermostat"
    optimistic: false
    retain: false
    qos: 0
    payload_on: 1
    payload_off: 0
    modes:
      - auto
      - heat
      - cool
      - 'off'
    mode_state_topic: "home/thermostat/temperaturemode" 
    mode_command_topic: "home/command/thermostat/temperaturemode"
    mode_state_template: >-
      {% if value == 0 %}
      auto
      {% elif value == 1 %}
      heat
      {% elif value == 2 %}
      cool
      {% elif value == 4 %}
      'off'
      {% endif %}
    fan_modes:
      - auto
      - 'on'
    fan_mode_state_topic: "home/thermostat/fancontrol"
    fan_mode_command_topic: "home/command/thermostat/fancontrol"
    fan_mode_state_template: >-
      {% if value == 0 %}
      auto
      {% elif value == 2 %}
      'on'
      {% endif %}
    current_temperature_topic: "home/thermostat/temperature"
    min_temp: 17
    max_temp: 28
    temperature_state_topic: "home/thermostat/currentsetpoint"
    temperature_command_topic: "home/command/thermostat/currentsetpoint"
    hold_command_topic: "home/command/thermostat/mode"
    hold_state_topic: "home/thermostat/mode"
    hold_state_template: >-
      {% if value == 2 %}
      hold
      {% else %}
      'off'
      {% endif %}

I looked through all of the config files in the Cookbook collection, searching for examples, but no one is using MQTT HVAC. The template tester indicates my templates are OK but apparently not.

Any clues to what I screwed up here?

Does the value come back as a string? I’m guessing so, you are very close. But for example, in jinja astring of a number does not equal a integer of a number. Also, you can make a dictionary where you pull out the information directly. It allows you to have an if statement where you return a default value for safety during startup.


This is a dictionary:

foo = {'a':1, 'b':2} 

and if I want 'a’s value:

foo['a']

returns 1.


This is a different way of doing quick if else statements:

normal if:

if x == 0:
  y = 0
else:
  y = 1

a quick and if statement that is equvialent is this:

y = 0 if x == 0 else 1 

It makes for quick decisive if statements without room for error. When you use if elif and else statements, you can leave holes. For example, you are missing else statements on a few of your if blocks, that will cause errors if that limb is reached.


so, try swapping them all to strings and you could use a dictionary as well:

climate:
  - platform: mqtt
    name: "Thermostat"
    optimistic: false
    retain: false
    qos: 0
    payload_on: 1
    payload_off: 0
    modes:
      - auto
      - heat
      - cool
      - off
    mode_state_topic: "home/thermostat/temperaturemode" 
    mode_command_topic: "home/command/thermostat/temperaturemode"
    mode_state_template: >-
      {% set values = { '0':'auto', '1':'heat',  '2':'cool', '4':'off'} %}
      {{ values[value] if value in values.keys() else 'off' }}
    fan_modes:
      - auto
      - on
    fan_mode_state_topic: "home/thermostat/fancontrol"
    fan_mode_command_topic: "home/command/thermostat/fancontrol"
    fan_mode_state_template: >-
      {% set values = { '0':'auto', '2':'on'} %}
      {{ values[value] if value in values.keys() else 'auto' }}
    current_temperature_topic: "home/thermostat/temperature"
    min_temp: 17
    max_temp: 28
    temperature_state_topic: "home/thermostat/currentsetpoint"
    temperature_command_topic: "home/command/thermostat/currentsetpoint"
    hold_command_topic: "home/command/thermostat/mode"
    hold_state_topic: "home/thermostat/mode"
    hold_state_template: "{{ 'hold' if value == '2' else 'off' }}"
2 Likes

Thank you for taking the time to post a detailed reply. Although I have a long way to go to become comfortable with jinja, I understand what you did. Not only is a dictionary a cleaner and more elegant way than if/elif/else, I’m happy to report it works!

FWIW, I had several permutations of integer/string including this one (where everything is explicitly string) yet it never worked. In theory it should work like the dictionary example you presented yet it did not. I won’t ask you to debug why this version did not work; your solution is better.

There is one last piece to this puzzle that maybe you can help me solve. When I change the Operation mode from Heat to Off, it naturally publishes ‘Off’ to the Mode topic. However, that’s meaningless to the thermostat which expects to receive a ‘4’ to turn off.

Whereas a template can be specified for received messages, none is supported for transmitted messages. This seems like a pretty common requirement, translating inbound and outbound payloads. So how does one translate ‘Off’ to ‘4’ for outbound messages?

FWIW, I thought I read a post (which I can now no longer find) where an automation was used to trigger on the outbound event and perform the conversion. However, I’m not 100% certain that was an applicable example and, at the time, it seemed like a roundabout way of doing it (compared to the simplicity of using a value_template).

Ok, I think you need to use value_template. From the documents, it alludes that all *_state_topic us this template when set.

climate:
  - platform: mqtt
    name: "Thermostat"
    optimistic: false
    retain: false
    qos: 0
    payload_on: 1
    payload_off: 0
    value_template: >-
      {% set values = {'auto':'0','heat':'1','cool':'2','off':'4','hold':'2','on':'2'} %}
      {{ values[value] if value in values.keys() else '0' }}
    modes:
      - auto
      - heat
      - cool
      - off
    mode_state_topic: "home/thermostat/temperaturemode" 
    mode_command_topic: "home/command/thermostat/temperaturemode"
    mode_state_template: >-
      {% set values = { '0':'auto', '1':'heat',  '2':'cool', '4':'off'} %}
      {{ values[value] if value in values.keys() else 'off' }}
    fan_modes:
      - auto
      - on
    fan_mode_state_topic: "home/thermostat/fancontrol"
    fan_mode_command_topic: "home/command/thermostat/fancontrol"
    fan_mode_state_template: >-
      {% set values = { '0':'auto', '2':'on'} %}
      {{ values[value] if value in values.keys() else 'auto' }}
    current_temperature_topic: "home/thermostat/temperature"
    min_temp: 17
    max_temp: 28
    temperature_state_topic: "home/thermostat/currentsetpoint"
    temperature_command_topic: "home/command/thermostat/currentsetpoint"
    hold_command_topic: "home/command/thermostat/mode"
    hold_state_topic: "home/thermostat/mode"
    hold_state_template: "{{ 'hold' if value == '2' else 'off' }}"

I added the value_template section (I think that’s the only difference between the two examples) but it had an unintended consequence. For some reason, it affects the temperature. Both current and target temperature are now set to 0 C. Looks like I’ll be saving a lot of energy today! :wink:

I’m guessing the value_template was also applied to the (inbound) temperature topic? There was no match in the dictionary for either 17.5 or 20.5 so it chose the default value of 0.

FWIW, the value of 0 C never got published so my actual thermostat is still showing 20.5 ambient and 17.5 setpoint. I’ve commented out the value_template for now.

I’m not sure why it was handled this way for the state_topics. Seems like a pain in the ass if you ask me. Anways, just change your template to this:

    value_template: >-
      {% set values = {'auto':'0','heat':'1','cool':'2','off':'4','hold':'2','on':'2'} %}
      {{ values[value] if value in values.keys() else value }}

That should do the trick for all your state_topics.

Partial success. Both temperatures are displayed correctly again but Operating mode still transmits the text string instead of an integer value. An additional quirk is I can select auto, cool, and off and HA will report (in the UI) that it is calling for a state change (and the receiving end gets the text string). However, if I choose heat, HA reports nothing and the receiving end gets nothing. I don’t know if its a clue or just a separate glitch. The main thing is that the value_template isn’t making MQTT HVAC translate outbound payloads.

As for ‘pain in the ass’, I agree 100%. I don’t have a lot of experience with Home Assistant but this component has proven to be the most challenging of all to configure.

Hmm, i’m not sure. I don’t have one of these devices. I would expect that to work. Maybe try:

value_template: >-
      {% set values = {'auto':'0','heat':'1','cool':'2','off':'4','hold':'2','on':'2'} %}
      {{ values[value.lower()] if value.lower() in values.keys() else value }}

You could also just change everything to numbers to see if it works with numbers everywhere

No joy; still sends the text and not the integer (and selecting Heat mode fails to trigger a state-change).

This evening I’ll try your suggestion to experiment with integer values in the dictionary list. Many thanks for your assistance. I wouldn’t have gotten this far without it. I’ll report my results later in the day. Even if there’s no solution, this thread may save someone else some time and effort.

After a lot of experimentation, trying all permutations of integer and string values, lower-case/upper-case, etc I’ve arrived at a conclusion. This platform can’t do what I need it to do. In fact, it surprises me anyone is using it. I’d love to hear about their use-case.

Basically, the templates the platform supports are exclusively for transforming the incoming data, namely the state topics. When the documentation says value_template is used for all *_state_topics (i.e. mode_state_topic_, fan_mode_state_topic_, etc), it’s 100% true; only state topics, not command topics.

If it receives a 0 you can use a template to convert it to OFF. However, when you select OFF from the selection-list, this platform will publish OFF and not 0. It does not support a template to convert OFF back to 0. I don’t understand the thinking behind this design decision. It seems like a significant oversight that really hamstrings this platform’s usability.

Anyway, the following yaml correctly displays the target temperature, operation mode, and fan mode. Temperature works because the received/transmitted payload is simply numeric. The other two don’t work because they are received as integers, templated to display as text, and then … there’s no way to template them back to integers to control the actual thermostat.

    climate:
      - platform: mqttnew
        name: "Thermostat"
        optimistic: false
        retain: false
        qos: 0
        payload_on: 1
        payload_off: 0
        modes:
          - auto
          - heat
          - cool
          - 'off'
        mode_state_topic: "home/thermostat/temperaturemode" 
        mode_command_topic: "home/command/thermostat/temperaturemode"
        mode_state_template: >-
          {% set values = { '0':'auto', '1':'heat',  '2':'cool', '4':'off'} %}
          {{ values[value] if value in values.keys() else 'off' }}
        fan_modes:
          - auto
          - 'on'
        fan_mode_state_topic: "home/thermostat/fancontrol"
        fan_mode_command_topic: "home/command/thermostat/fancontrol"
        fan_mode_state_template: >-
          {% set values = { '0':'auto', '2':'on'} %}
          {{ values[value] if value in values.keys() else 'auto' }}
        current_temperature_topic: "home/thermostat/temperature"
        min_temp: 17
        max_temp: 28
        temperature_state_topic: "home/thermostat/currentsetpoint"
        temperature_command_topic: "home/command/thermostat/currentsetpoint"
        hold_command_topic: "home/command/thermostat/mode"
        hold_state_topic: "home/thermostat/mode"
        hold_state_template: "{{ 'hold' if value == '2' else 'auto' }}"

I’m going to study how other MQTT components are constructed but I get the feeling a thermostat is probably the most complex of all and requiring the most flexibility. All this to say, the other examples might not give me any insight into how to fix this.

So what you could do is use the numbers for everything, then build input_select’s that control the mqtt climate entity. It wouldn’t be too hard to make that work. Just a few automations. I can help if you like.

I’m pleased to report that I’ve successfully modified MQTT HVAC and it now supports templates for command topics. I learned how it uses templates for state topics and it was fairly easy to extend it to do the same magic for command topics. So now I can specify:

  • state topic and state template (existing capability)
  • command topic and command template (new capability)

Here’s a sample:

    modes:
      - auto
      - heat
      - cool
      - 'off'
    mode_state_topic: "home/thermostat/temperaturemode" 
    mode_state_template: >-
      {% set values = { '0':'auto', '1':'heat',  '2':'cool', '4':'off'} %}
      {{ values[value] if value in values.keys() else 'off' }}
    mode_command_topic: "home/command/thermostat/temperaturemode"
    mode_command_template: >-
      {% set values = { 'auto':'0', 'heat':'1',  'cool':'2', 'off':'4'} %}
      {{ values[value] if value in values.keys() else '4' }}

    fan_modes:
      - auto
      - 'on'
    fan_mode_state_topic: "home/thermostat/fancontrol"
    fan_mode_state_template: >-
      {% set values = { '0':'auto', '1':'on'} %}
      {{ values[value] if value in values.keys() else 'auto' }}
    fan_mode_command_topic: "home/command/thermostat/fancontrol"
    fan_mode_command_template: >-
      {% set values = { 'auto':'0', 'on':'1'} %}
      {{ values[value] if value in values.keys() else '0' }}

Works like a charm. Now I just need to figure out why the UI doesn’t render a widget for controlling the Hold mode. One problem at a time! :slight_smile:

EDIT
It appears the reason why there’s no Hold mode in the UI is because the frontend code does not support it. A quick glance through the code shows nothing for rendering a Hold widget.

Thank you and, yes, I would still like to learn how to do that. I haven’t explored input_select or automations yet so this is a perfect opportunity.

2 Likes

Hi

Could you share your MQTT HVAC version that supports command templates? I have similar issue that you had, so it will be very helpful to me if I can benefit from your work.

Thanks

Yes, of course. However, I’ll need another or day or two before I can share it because I’m in the middle of enhancing it and haven’t tested it fully.

  • This version of MQTT HVAC supports command templates for mode, fan_mode, and hold_mode.
  • Default temperature_step is 0.5 for Celsius and 1 for Fahrenheit (automatically configured). Alternately, it can be specified with temp_step in configuration.yaml.

https://pastebin.com/yfVPJfj0

1 Like

I like the changes that you have made. I was also looking for the mode_command_template and fan_mode_command_template capability. Is there a reason you didn’t implement a temperature_command_template too? My HVAC system uses Celsius while my user interface utilizes Fahrenheit. For the mqtt sensors I’ve been converting the values back and forth when retrieving and saving the values to mqtt. Looking to do the same here. I already doing it when retrieving the values using a temperature_state_template.

The HVAC system that I’m using also supports two separate set points. One for cool and the other for heat. I’m looking at doing further modes to support dual set points. Any suggestions would be appreciated.

I also saw another post about the component showing when the system is actually running as opposed to just the operation state. It looks like you made that change a few days after you posting this version. Do you have that and/or other enhancements available?

Thanks!

Short answer: I didn’t need it. I extended the platform to suit my own needs.

Having said that, you’ve provided a compelling example of why a temperature_command_template would be useful. When I have the time, I’ll toss that one into the mix and post it in this thread: Enhanced version of MQTT HVAC (Climate platform) with proper History Chart

I believe the low/high temperature control is in the Pull Request pipeline: Add target temperature low high to MQTT climate by rwagoner · Pull Request #17391 · home-assistant/core · GitHub

1 Like

I went ahead and download the updated version of the MQTT HFAC component with the low/high temperature control. Works great. I went ahead and updated it to include the following command templates:

mode_command_template
temperature_command_template
temperature_low_command_template
temperature_high_command_template
fan_mode_command_template
swing_mode_command_template
hold_command_template

Here is a link to my updated version: mqtt.py

You had some of these templates covered in your version. I added those and others to the version posted. I’d be interested incorporating your additional updates too. Please share.

I’m new to HA development, so I don’t know the best way to get these incorporated into a released version.

1 Like

How do I install this? I need mode_command_template …