Understanding state_topic

Hi all,

I would like to get state_topic working on my switch, and would love some feedback.

I have an MQTT switch that sends messages to my ESP32 Arduino board to turn irrigation valves on and off – this works fine. I would like to make it a closed loop system: have the switch receive a message that the action has been carried out, and only then would the HA dashboard show the change in state.

I have set it up so that the ESP32 sends out the MQTT confirmation when it receives the command – this also works fine. However, the HA dashboard does not react to the message at all. Here’s the part of my config:

switch:
# Arduino switches for four garden valves
  - platform: mqtt
    name: 'In1'
    state_topic: 'garden/state'
    command_topic: 'garden/valve1'
    retain: true
    state_on: '{"valve": "In1", "value": 1}'
    state_off: '{"valve": "In1", "value": 0}'
    payload_on: '{"valve": "In1", "value": 1}'
    payload_off: '{"valve": "In1", "value": 0}'
    icon: mdi:water

I’ve tried with and without the state_on / _off lines, and have restarted HA, but no luck. I’m not even sure where to look. Does anyone have tips on how to debug this?

cheers,
Sean

could you post here what messages do you receive in garden/state?

you always have to restart HA for changes to have effect in your case.

Update: yes, this seems to be the issue. It works with ON / OFF, but not with json. A bit of a shame, but workable.

Yes, thanks, I have been restarting. I receive:

garden/state {“valve”:“In1”,“value”:1} and
garden/state {“valve”:“In1”,“value”:0}

With more playing around, it seems like state_on / _off might not accept json? I’m trying now with just ON and OFF.

cheers,
Sean

No, they accept JSON, you just need a value_template to extract the relevant string.

Interesting, thanks Tom. Any idea why I need a value template for state_on but not for payload_on?

You shouldn’t actually need state_on and state_off as they are the same as the payload_on/off. https://www.home-assistant.io/integrations/switch.mqtt/#state_off

Yes, clear. I had it in there as I was testing ON/OFF instead of the payload.

That doesn’t seem to change whether state_topic accepts json without a template. I’ve tried a few variants, including leaving out state_on/off, but it does not make a difference.

It is a shame, but seems my original conclusion is correct: payload_ accepts json without a template, but state_ does not.

I think you are still not clear about how MQTT template switch works.
It uses state topic to get state of the switch and command topic to control it.

There are 2 different cases

  1. You receive garden/state RUN or garden/state IDLE when your switch is on or off and you send garden/command RUN or garden/command IDLE to turn your switch on or off.
  2. You receive garden/state RUN or garden/state IDLE when your switch is on or off and you send garden/command TURN_ON or garden/command TURN_OFF to turn your switch on or off.

As you can see, the difference is in what strings you need to look for when analysing state topic and what you need to send to command topic. These strings are called payloads.

In the first case payloads for both topics are the same. However, you need to specify payload_on and payload_off as they are different from the default ones (ON/OFF):

payload_on: RUN
payload_off: IDLE

In the second case in addition to payload_xxx you need to define state_xxx as payload to turn your switch (TURN_ON ) is different from the payload that represents the switch’s on state in the state topic (RUN):

payload_on: TURN_ON
payload_off: TURN_OFF
state_on: RUN
payload_off: IDLE

And you need to define how to extract your payload from incoming messages in state topic as it’s JSON, not a simple string. So you should have:

value_template: {{ value_json['value'] }}

The bottom line is: payload_xxx and state_xxx are strings (so they cannot acccept anything) and these strings are used to match with the result of value_template.

Hope it is clear now.

Hi Ahmad,

Thanks, that is useful – it may be that I will need to learn how to use the templates and I will then use your explanation. Up until now, I have been resisting templates, as I am already learning too many things at once and want to limit the complexity.

My point was that I don’t use a value_template at all today. Nonetheless, payload_on: ‘{“valve”: “In1”, “value”: 1}’ works fine. Also, state_on: ‘ON’ works fine.

My point was not about how templates work. I was saying that I find it a shame that state_on requires a value_template while payload_on doesn’t. In any case, my solution of sending both messages does work without using a template.

regards,
Sean

I appreciate that. Take your time.

Could you post here your configs illustrating what you did and what’s “fine”? Especially with state_on.

I can only re-iterate that you need to take your time learning the docs and idea behind it.
If you post your config, I’ll explain you how to use state_xx with or without templates as it’s not necessary (value_template is an optional variable).

Thank you for your patience, Ahmad.

Here is what I have running for the last year. It controls the valves, but does not give confirmation from the device. I didn’t use a template:

switch:
# Arduino switches for four garden valves
  - platform: mqtt
    name: 'In1'
    command_topic: 'garden/valve1'
    retain: true
    payload_on: '{"valve": "In1", "value": 1}'
    payload_off: '{"valve": "In1", "value": 0}'
    icon: mdi:water

Here is what I use now. I send the state topic from the device as confirmation, and HA understands the state reply:

switch:
# Arduino switches for four garden valves
  - platform: mqtt
    name: 'In1'
    state_topic: 'garden/state/v1'
    state_on: 'ON'
    state_off: 'OFF'
    command_topic: 'garden/valve1'
    retain: true
    payload_on: '{"valve": "In1", "value": 1}'
    payload_off: '{"valve": "In1", "value": 0}'
    icon: mdi:water

What I wanted to do was use json also for the state topic. I suspect this is not possible without a template, but this is what I wanted:

switch:
# Arduino switches for four garden valves
  - platform: mqtt
    name: 'In1'
    state_topic: 'garden/state/v1'
    state_on: '{"valve": "In1", "value": 1}'
    state_off: '{"valve": "In1", "value": 0}'
    command_topic: 'garden/valve1'
    retain: true
    payload_on: '{"valve": "In1", "value": 1}'
    payload_off: '{"valve": "In1", "value": 0}'
    icon: mdi:water

You don’t need to define a state on /off. It is the same as the payloads. That is what will be used to determine the state feedback.

do you send ON or OFF to garden/state/v1 here?

like garden/state {“valve”:“In1”,“value”:1}?
4 question:

  1. why do you need JSON here?
  2. have you changed message format your device posts?
  3. have you tried using a different key instead of value?
  4. do I get it right that your last config does not show your device’s state?

tom is right (and the docs say that) - you only need state_xx if your state payload is not the same as your command payload. In your case it’s enough to define only payload_xx - have you tried to remove state_xx?

Hi Tom,
I get the feeling that you have not actually read my posts. Could you elaborate?

yes, I send ON/OFF to v1

  1. I don’t absolutely need json, it would make my program simpler. As I wrote, I have found a workaround.
  2. I have not changed anything except what I have shown.
  3. Yes, no json works for state
    4.yes

I think you and Tom are missing the point. The question is whether state reads json or not, not whether it can be read. Documentation or no, I don’t believe state can read json without a template.

None of those payloads are JSON. They are strings that look like JSON.

Your initial aim was for closed loop control.

What I am trying to get through to you is you already had closed loop control when your command and state payloads were the same. The state messages were taken from the payload messages. There is no need to define state_on, or state_off in this case which is why it would not work for you unless you changed them to be different from the payload messages.

then you don’t need to declare state_xx as you send default values, try removing it in v1 and I bet it will still work as expected.

So you changed your config and it now expects {"valve": "In1", "value": 1} instead of ON in the state topic but you haven’t changed what your device publishes? Then your MQTT switch won’t be able to match ON (what you’re still publishing) with {"valve": "In1", "value": 1} (what you instructed it to look for in the state topic) and HA won’t display your device’s state correctly. And it should generate No matching payload in HA logs for that reason, too.
In this situation you have to change code of your device so instead of ON it sends {"valve": "In1", "value": 1}.

Ok. Could you explain as what exactly does"state reads json" mean?

Hi Tom,

I’m not sure what you mean by “you already had closed loop control when your command and state payloads were the same.” There would need to be a response from my irrigation controller for me to be sure that the command from HA was received and implemented. How do you think that has happened?

I think your point about “None of those payloads are JSON. They are strings that look like JSON.” was indeed my confusion. It was not “reading json” in payload_on, it was simply sending a text string that looked like json. That is what had me confused, as it seemed to use json in one case and not the other.

So you changed your config and it now expects {"valve": "In1", "value": 1} instead of ON in the state topic but you haven’t changed what your device publishes?

Apologies, that wasn’t precise enough. Yes, I also changed what the device publishes.

Could you explain as what exactly does"state reads json" mean?

Again, leaving aside the point the I probably need to learn to use a template, what I mean is that when I send ON/OFF to the state_topic it reacts. When I send ‘{“valve”: “In1”, “value”: 1}’ it does not. Actually, I think Tom’s point here is behind my misunderstanding: it was not “reading json” in payload_on, it was simply sending a text string that looked like json. That is what had me confused, as it seemed to use json in one case and not the other; rather, it seems it will not “use” json without the template.

Ok for the last time then you’re on your own.

When the payloads for command and state are the same ( as you had them originally) you don’t need to specify state_on and state_off. It uses payload_on and payload_off by default. It’s right there in black and white in the document I linked to above.

state_on

(string)(Optional)

The payload that represents the on state. Used when value that represents on state in the state_topic is different from value that should be sent to the command_topic to turn the device on .

Default value:

payload_on if defined <----######## look #####