btsimonh/Tasmota

TRV: Need more consistent MQTT messages

Ysbrand opened this issue · 11 comments

At the moment it's pretty tough to parse MQTT messages as they do not contain a single shared field.

Topic:
stat/ESP32-1/EQ3/001A2216A458

A regular parse able json

{
"trv":"00:1a:22:16:a4:58",
"blestate":"DONENOTIFIED",
"raw":"020108040428000000001803231607",
"temp":20.0,
"posn":4,
"mode":"auto",
"boost":"inactive",
"dst":"set",
"window":"closed",
"state":"unlocked",
"battery":"GOOD",
"holidayend":"00-00-00 00:00"
}
{
"trv":"00:1a:22:16:3f:0a",
"blestate":"DONENOTIFIED",
"raw":"21010A212630166626840A9022902290",
"profileday1":"5.0-05:30,19.0-08:00,11.0-17:00,19.0-22:00,5.0-24:00,17.0-24:00,17.0-24:00"
}

And the one causing problems

{
"result":"queued"
}

Suggest to have something like:

{
"trv":"00:1a:22:16:a4:58",
"blestate":"queued",
}

In other words, every message has at least the mac address and a status field.

Background:
While I'm trying to check for the contents of a field in HA, it causes error messages and filling up the logs. SO I can still get things to work but not in a clean way. Might be my lack of knowledge on HA.

(And I think the same applies to the tele/.../BLE messages)

This issue has been automatically marked as stale because it hasn't any activity in last few weeks. It will be closed if no further activity occurs. Thank you for your contributions.

still under review... posting to keep active

  • platform: mqtt
    name: werkkamer_radiator_climate
    modes:
    • "auto"
    • "off"
    • "heat"
      min_temp: 4.5
      max_temp: 23.0
      temp_step: 0.5
      mode_command_topic: cmnd/ESP32-1/EQ3/001A2216A458/mode
      temperature_command_topic: cmnd/ESP32-1/EQ3/001A2216A458/settemp
      current_temperature_topic: tele/ESP32-1/SENSOR
      current_temperature_template: >
      {% if value_json["ATC0137cb"] is defined %}
      {{ value_json["ATC0137cb"]["Temperature"] }}
      {% endif %}
      temperature_state_topic: stat/ESP32-1/EQ3/001A2216A458

    temperature_state_template: '{{ value_json.temp }}'

    temperature_state_template: >
    {% if value_json.result != "queued" %}
    {{ value_json.temp }}
    {% else %}
    {{ state_attr('climate.werkkamer_radiator_climate','temperature') }}
    {% endif %}
    mode_state_topic: stat/ESP32-1/EQ3/001A2216A458
    mode_state_template: >
    {% if value_json.mode == "auto" %}
    auto
    {% elif value_json.temp == "4.5" %}
    'off'
    {% elif value_json.valve > "5" %}
    heat
    {% endif %}
    precision: 0.5

Test Discovery messages:

adds an attr entity as a sensor

{
"topic":"homeassistant/sensor/EQ3001A22092CDB/attr/config",
"payload":"{"device":{"name":"EQ3001A22092CDB","identifiers":["001A22092CDB"],"mf":"EQ3","mdl":"EQ3"},"unique_id":"001A22092CDBattr","state_topic":"stat/EQ3/001A22092CDB","name":"EQ3001A22092CDB_attr","value_template":"{{ value_json.mode }}","json_attributes_topic":"stat/EQ3/001A22092CDB"}"
}

adds the climate entity with presets and 'hvac action' (off/heating):

{
"topic":"homeassistant/climate/EQ3001A22092CDB/control/config",
"payload":"{"device":{"name":"EQ3001A22092CDB","identifiers":["001A22092CDB"],"mf":"EQ3","mdl":"EQ3"},"name":"EQ3001A22092CDB","max_temp":30,"min_temp":5,"temperature_command_topic":"cmnd/tasmota_E89E98/EQ3/001A22092CDB/settemp","mode_command_topic":"cmnd/tasmota_E89E98/EQ3/001A22092CDB/mode","modes":["off","heat","auto","idle"],"temperature_unit":"c","temp_step":"0.5","precision":0.5,"unique_id":"001A22092CDB","action_topic":"stat/EQ3/001A22092CDB","action_template":"{{ 'off' if value_json.posn < 25 else 'heating' }}","hold_command_topic":"cmnd/tasmota_E89E98/EQ3/001A22092CDB","hold_state_template":"{{ value_json.mode }}","hold_state_topic":"stat/EQ3/001A22092CDB","hold_modes":["auto","manual","off","on","boost","unboost","lock","unlock","day","night","reqprofile 0"],"current_temperature_topic":"tele/tasmota_ble/ATC214e0d","current_temperature_template":"{{ value_json.Temperature }}","temperature_state_topic":"stat/EQ3/001A22092CDB","temperature_state_template":"{{ value_json.temp }}","mode_state_topic":"stat/EQ3/001A22092CDB","mode_state_template":"{{ value_json.hassmode }}"}"
}

This issue was automatically closed because of being stale. Feel free to open a new one if you still experience this problem.

@btsimonh Hi Simon, how do I activate the test HA Discovery Messages on the latest EQ3 branch?

ahh.. we've not quite got there yet.
I have a node-red flow which detects EQ3 mqtt messages, and then periodically sends HA discovery messages.
@Ysbrand configured his manually for the moment.
The HUGE step forward we've made is in HA itself in lovelace.
If you are not already using the 'decluttering' card - look it up - @Ysbrand was using the mini-climate card, but configuration of it is complex (a page of yaml per EQ3), but with the decluttering card, you do that once, and then each eq3 is 3-4 lines :)

p.s. make sure you update to a recent push - we found and fixed an issue which was causing eq3 polling to stall (thanks ysbrand for all your help!).

If you want the NR flow, i'll post it here; but my pi was not active this morning and seems non boot - I thought HA on an rpi4 with SSD was going to solve all the instability issues of my rpi3+SD, but maybe not :(. So when it's back up and I can lift the flow....

p.p.s you can send the above manually? But you may need to use the Alias rather than the mac with latest EQ3 branch.

damn. it did not go down - it changed IP! (router reboot) lucky it's not my primary HA for the moment.

To get you going, it's not perfect yet, but pretty useable

disable BLE in the main HAS BLE configuration and save (we're enabling BLE in the autoexec.bat otherwise you might get unwanted reports before we're finished setting up the aliases.

I'm using aliases, you can set them up in an autoexec.bat (new 93.1 TAS feature).

BLEAlias 001A2216A458=EQ3werkkamer
BLEAddrFilter 0
MI32Option6 1

bleenableunsaved 1

Climate configuration:

- platform: mqtt
  name: werkkamer_radiator_climate
  modes: ['off',heat,auto,idle]
  min_temp: 4.5
  max_temp: 23.0
  temp_step: 0.5
  mode_command_topic: cmnd/eq3_devices/EQ3/EQ3werkkamer/mode
  temperature_command_topic: cmnd/eq3_devices/EQ3/EQ3werkkamer/settemp
  current_temperature_topic: tele/tasmota_ble/ATC0137cb
  current_temperature_template: '{{ value_json.Temperature }}'
  temperature_state_topic: stat/EQ3/EQ3werkkamer
  temperature_state_template: '{{ value_json.temp }}'
  mode_state_topic: stat/EQ3/EQ3werkkamer
  mode_state_template: '{{ value_json.hassmode }}'
  hold_command_topic: cmnd/eq3_devices/EQ3/werkkamer
  hold_state_template: '{{ value_json.mode }}'
  hold_state_topic: stat/EQ3/werkkamer
  hold_modes: ["auto","manual","off","on","boost","lock","unlock","day","night"]
  precision: 0.5

the tele/tasmota_ble/ATC0137cb is used for the actual room temperature as it is not reported by the EQ3

sensors:

- platform: mqtt
  name: "werkkamer_valve"
  state_topic: stat/EQ3/EQ3werkkamer
  value_template: >
    {% if value_json.result == "ok" %}
      {{ value_json.posn }}
    {% else %}
      {{ states('sensor.werkkamer_valve') }}
    {% endif %}
  unit_of_measurement: "%"

And if you want to use the decluttering template with the mini climate card (very nice if you need to configure multiple trv's in lovelace):

  template_heating:
    card:
      type: 'custom:mini-climate'
      entity: 'climate.[[trv_name]]_radiator_climate'
      name: '[[nice_name]]'
      icon: 'mdi:radiator'
      secondary_info: hvac-mode
      tap_action:
        action: more-info
        entity: 'sensor.[[trv_name]]_valve'
      hvac_mode:
        style: '(value, entity) => ({ color: "black" })'
        source:
          'off':
            icon: 'mdi:power'
            name: 'off'
          idle:
            icon: 'mdi:fan'
            name: idle
          heating:
            icon: 'mdi:weather-sunny'
            name: heat
          auto:
            icon: 'mdi:clock-outline'
            name: auto
      temperature:
        unit: °C
        fixed: 1
        source:
          entity: 'sensor.[[sensor_name]]_temperature'
      fan_mode:
        hide: 'off'
        icon: 'mdi:fan'
        order: 0
        state:
          attribute: preset_mode
        active: '(state, entity) => true'
        source:
          state: state
          auto: auto
          manual: manual
          'off': 'off'
          'on': 'on'
          Boost: boost
          unboost: unboost
          lock: lock
          unlock: unlock
          day: day
          night: night
        change_action: >
          (selected, state, entity) => this.call_service('climate',
          'set_preset_mode', { entity_id: entity.entity_id, preset_mode:
          selected })
      # Hide 3 dots menu
      # fan_mode:
        # hide: on
      indicators:
        valve:
          icon:
            template: '() => "mdi:valve"'
            style: >
              (value) => (value > 80 ? { color: 'red'} 
                : value > 40 ? { color: 'orange'}
                : value > 0 ? { color: 'yellow'}
                : { color: 'green' })
          unit: '%'
          round: 1
          source:
            entity: 'sensor.[[trv_name]]_valve'
        humidity:
          icon:
            template: '() => "mdi:water"'
            style: >
              (value) => (value > 80 ? { color: 'darkblue'} 
                : value > 50 ? { color: 'blue'}
                : value > 40 ? { color: 'lightblue'}
                : { color: 'grey' })
          unit: '%'
          round: 0
          source:
            entity: 'sensor.[[sensor_name]]_humidity'

and the lovelace code:

    - type: custom:decluttering-card
      template: template_heating
      variables:
        - trv_name: werkkamer
        - nice_name: Werkkamer
        - sensor_name: atc_werkkamer

and the NR flow snippet.

Note: @Ysbrand and i have not yet agreed on the same terms of some HA variables - i.e. the below does NOT match the above without modification :( (e.g. i used 'posn' ysbrand used 'valve').

Ref HA discovery my brain is playing around with the idea of a much simplified 'discovery' message which contains a 'unique template' ref, and just the minimum information, which would then be expanded into one or multiple HA discovery messages.
Then people could modify the template to do what they need without re-compilation of things like TAS. It's only a thought at the moment, as ideally it would be integrated into the HA MQTT discovery - but i'm not happy going near actual HA python dev :(. NR in between is an option; so a bit like the flow below, but with an additional field which defines which 'template' to use.

[{"id":"b33c9bee.52c758","type":"function","z":"61bb7c2f.f3daf4","name":"sendEQ3Discovery","func":"//let scanner = flow.get('scanner');\n//let templates = global.get('tasmotatemplates');\n\n//if (!scanner) return;\n\n\n//msg.payload = scanner;\n\n\nlet sensormap = global.get('sensormap') || {};\n\n// map of EQ3 names to ATC temp sensor topics \n// so that we can have current_temp\nsensormap = {\n    EQ3LPatio:'tele/tasmota_ble/LvngRoom',\n    EQ3LDoor:'tele/tasmota_ble/LvngRoom',\n    EQ3Dining:'tele/tasmota_ble/Dining',\n    EQ3Bed1:'tele/tasmota_ble/Bedroom1',\n    EQ3Bed2:'tele/tasmota_ble/Bedroom2',\n    EQ3Bed3:'tele/tasmota_ble/Bedroom3',\n    EQ3Bed4:'tele/tasmota_ble/Bedroom4',\n    EQ3Landing:'tele/tasmota_ble/Landing',\n}\n\n\nglobal.set('sensormap', sensormap);\n\n\n//let successes = Object.keys(scanner.success);\n\n/*\n11:28:42.924 MQT: stat/EQ3/001A22092CDB = \n{\"cmd\":\"state\",\n\"result\":\"ok\",\n\"RSSI\":-83,\n\"stattime\":1613816922,\n\"temp\":21.0,\n\"posn\":49,\n\"mode\":\"auto\",\n\"boost\":\"inactive\",\n\"dst\":\"set\",\n\"window\":\"closed\",\n\"state\":\"unlocked\",\n\"battery\":\"GOOD\"}\n*/\n\nlet tsplt = msg.topic.split('/');\n\n\nlet mac = tsplt[2];\nlet DeviceName = \"EQ3\"+mac;\nlet statname = 'RSSI'; \nlet jsonname = statname;\nlet device_class = 'signal_strength';\nlet negative = false;\n\nnode.warn(mac);\n\nlet sensormap = global.get('sensormap') || {};\n\nlet curr_temp_topic = sensormap[mac];\n\nif (!curr_temp_topic){\n    curr_temp_topic = 'dummy/dummy';\n}\n\n//return;\n\n\n\nlet outs = [];\n\nlet out =\n{\n    \n    topic: 'homeassistant/sensor/'+DeviceName+'/'+statname+'/config',\n    payload:{\n        device:{\n            name: DeviceName,\n            identifiers: [mac],\n            mf: 'EQ3',\n            mdl: 'EQ3',\n        },\n        unique_id: mac+statname,\n        device_class: device_class,\n        //stat/EQ3/001A22092CDB\n        state_topic: 'stat/EQ3/'+mac,\n        name: DeviceName+'_'+statname,\n        unit_of_measurement: 'dB',\n        value_template: \"{{ \"+(negative?'-':'')+\"value_json.\"+jsonname.split('_').join('.')+\" }}\",\n\n    }\n};\n\nouts.push(JSON.parse(JSON.stringify(out)));\nstatname = 'setpoint'; \njsonname = 'temp'; \ndevice_class = 'temperature';\n\nout =\n{\n    topic: 'homeassistant/sensor/'+DeviceName+'/'+statname+'/config',\n    payload:{\n        device:{\n            name: DeviceName,\n            identifiers: [mac],\n            mf: 'EQ3',\n            mdl: 'EQ3',\n        },\n        unique_id: mac+statname,\n        device_class: device_class,\n        //stat/EQ3/001A22092CDB\n        state_topic: 'stat/EQ3/'+mac,\n        name: DeviceName+'_'+statname,\n        unit_of_measurement: 'C',\n        value_template: \"{{ \"+(negative?'-':'')+\"value_json.\"+jsonname.split('_').join('.')+\" }}\",\n    }\n};\n\nouts.push(JSON.parse(JSON.stringify(out)));\n\nstatname = 'posn'; \ndevice_class = 'temperature';\njsonname = statname;\n\nout =\n{\n    topic: 'homeassistant/sensor/'+DeviceName+'/'+statname+'/config',\n    payload:{\n        device:{\n            name: DeviceName,\n            identifiers: [mac],\n            mf: 'EQ3',\n            mdl: 'EQ3',\n        },\n        unique_id: mac+statname,\n        device_class: device_class,\n        //stat/EQ3/001A22092CDB\n        state_topic: 'stat/EQ3/'+mac,\n        name: DeviceName+'_'+statname,\n        unit_of_measurement: '%',\n        value_template: \"{{ \"+(negative?'-':'')+\"value_json.\"+jsonname.split('_').join('.')+\" }}\",\n    }\n};\n\nouts.push(JSON.parse(JSON.stringify(out)));\n\n\n\n//\n// create a special entity just foe json attributes\nstatname = 'attr'; \n//device_class = 'temperature';\njsonname = 'mode';\n\nout =\n{\n    topic: 'homeassistant/sensor/'+DeviceName+'/'+statname+'/config',\n    payload:{\n        device:{\n            name: DeviceName,\n            identifiers: [mac],\n            mf: 'EQ3',\n            mdl: 'EQ3',\n        },\n        unique_id: mac+statname,\n        //device_class: device_class,\n        //stat/EQ3/001A22092CDB\n        state_topic: 'stat/EQ3/'+mac,\n        name: DeviceName+'_'+statname,\n        //unit_of_measurement: '%',\n        value_template: \"{{ value_json.\"+jsonname.split('_').join('.')+\" }}\",\n\n        json_attributes_topic: 'stat/EQ3/'+mac,\n    }\n};\n\nouts.push(JSON.parse(JSON.stringify(out)));\n\n\n\n\n\nstatname = 'control'; \n//device_class = 'temperature';\njsonname = statname;\n\nout =\n{\n    topic: 'homeassistant/climate/'+DeviceName+'/'+statname+'/config',\n    payload:{\n        device:{\n            name: DeviceName,\n            identifiers: [mac],\n            mf: 'EQ3',\n            mdl: 'EQ3',\n        },\n        name: DeviceName,\n        max_temp:30,\n        min_temp:5,\n        //aux_stat_t:msg.topic,\n        //aux_stat_tpl:'{{ value_json.posn }}',\n        temperature_command_topic: 'cmnd/tasmota_E89E98/EQ3/'+mac+'/settemp',\n        mode_command_topic: 'cmnd/tasmota_E89E98/EQ3/'+mac+'/mode',\n        modes: ['off','heat','auto','idle'],\n        temperature_unit: 'c',\n        temp_step:'0.5',\n        precision:0.5,\n        unique_id: mac,\n\n\n        // convert valve to 'off','idle','heating'\n        // valid: off, heating, cooling, drying, idle, fan\n        action_topic: 'stat/EQ3/'+mac,\n        action_template:\"{{ 'off' if value_json.posn < 25 else 'heating' }}\",\n        //action_template:\"{% if value_json.posn > 25 %}heating{% else %}off{% endif %}\",\n\n        // if these are present, hold is enabled\n        hold_command_topic:'cmnd/tasmota_E89E98/EQ3/'+mac,\n        hold_state_template:'{{ value_json.mode }}',\n        hold_state_topic: 'stat/EQ3/'+mac,\n        hold_modes: ['auto','manual','off','on','boost','unboost','lock','unlock','day','night','reqprofile 0'],\n    \n        \n        current_temperature_topic: curr_temp_topic,\n        current_temperature_template:'{{ value_json.Temperature }}',\n\n        temperature_state_topic: 'stat/EQ3/'+mac,\n        temperature_state_template:'{{ value_json.temp }}',\n        \n        mode_state_topic: 'stat/EQ3/'+mac,\n        mode_state_template:'{{ value_json.hassmode }}',\n        \n        // if these are present, presets is enabled\n        //away_mode_state_topic: 'stat/EQ3/'+mac,\n        //away_mode_state_template:'{{ value_json.mode }}',\n        //away_mode_command_topic: 'cmnd/tasmota_E89E98/EQ3/'+mac+'/mode',\n\n        //json_attributes_topic: 'stat/EQ3/'+mac,\n        //json_attributes_template:'{{ value_json.cmd | tojson }}',\n        \n    //    json_attributes_topic:msg.topic,\n    //    json_attributes_template:\n    //        '{{ {\"position\": value_json.posn, \"window\": value_json.window } | tojson }}'\n        \n    },\n};\n\nouts.push(JSON.parse(JSON.stringify(out)));\n\n\nfor (let i = 0; i < outs.length; i++){\n    let newmsg = {\n        topic: outs[i].topic,\n        payload: JSON.stringify(outs[i].payload),\n    };\n    node.warn(outs[i].payload);\n    node.send(newmsg);\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":510,"y":1040,"wires":[["3cdc4da.237cfb2","7ce94ebd.9dbbc"]]},{"id":"f2a8aca3.2afc","type":"inject","z":"61bb7c2f.f3daf4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":1040,"wires":[["b33c9bee.52c758"]]},{"id":"f198bf60.27b8f","type":"mqtt out","z":"61bb7c2f.f3daf4","name":"test","topic":"","qos":"0","retain":"false","broker":"eb6dc272.b4d74","x":970,"y":1040,"wires":[]},{"id":"3cdc4da.237cfb2","type":"debug","z":"61bb7c2f.f3daf4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":960,"y":1000,"wires":[]},{"id":"7ce94ebd.9dbbc","type":"delay","z":"61bb7c2f.f3daf4","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"0.1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":810,"y":1040,"wires":[["f198bf60.27b8f"]]},{"id":"d79f595b.7a7fc8","type":"mqtt in","z":"61bb7c2f.f3daf4","name":"","topic":"stat/EQ3/#","qos":"2","datatype":"auto","broker":"eb6dc272.b4d74","x":100,"y":960,"wires":[["b33c9bee.52c758","3cdc4da.237cfb2"]]},{"id":"eb6dc272.b4d74","type":"mqtt-broker","name":"HASS","broker":"127.0.0.1","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

I realise this is closed but I'd like to put my twopennorth in too.
I'm running ESP32-MQTT-EQ-3 and desperate to move over to Tasmota for my EQ-3s.
The issue with MQTTtoEQ-3 is the 'error' message when the device is unavailable and I'd like to make sure you don't make the same mistake.
This is a normal message
trv: "00:1A:22:12:65:E0" temp: "17.0" offsetTemp: "0.0" valve: "0% open" mode: "auto" boost: "inactive" window: "closed" state: "locked" battery: "GOOD"
And this is an error message
"trv":"00:1A:22:12:65:45" "error":"Device unavailable"
Because the keys are different it means that I can't read directly into openHAB (and I presume that HA would have the same issue) because the JSONPATH fails. It means I have to have an overly complicated node-red flow just to ensure that the JSONPATH parses correctly.
Ideally, I would like to see the following for a normal message
trv: "00:1A:22:12:65:E0" error: "N" temp: "17.0" offsetTemp: "0.0" valve: "0% open" mode: "auto" boost: "inactive" window: "closed" state: "locked" battery: "GOOD"
and this for an error message
trv: "00:1A:22:12:65:E0" error: "Y" temp: "0" offsetTemp: "0" valve: "0" mode: "" boost: "" window: "" state: "" battery: ""
That would ensure that the JSONPATH would always parse.