MQTT State Stream to MQTT Discovery

Totally inspired by the work of @RobDYI in Automating the sharing of sensors and switches between HA's using MQTT Discovery and Statestream, this blueprint creates topics compatible with MQTT Discovery for the entries generated by MQTT Statestream.

It generates entries for sensors, binary_sensors, light and switches. Furthermore, it also manages a command topic for on/off handling of lights and switches.

Due to current limitations with blueprints and MQTT automation triggers, I cannot create a parameter for the base topic to use, so it is hardcoded to “ha_stream”, and thus the “base_topic” of your mqtt_statestream must be set to that value.

Update: As of 2021.3, we can use trigger variables, so you can now specify the base topic in the blueprint
Update 27/06/2021: Now publishes attributes as well
Update 27/06/2021: bugfix
Update 27/06/2021: Optimalisation (thanks.@123 ). Does not need statestream attributes anymore.
Update 08/07/2021: bugfix (thanks @MoTarek)
Update 14/09/2021: fix for non-existing state_class
Update 18/10/2021: DEPRECATED: See MQTT DiscoveryStream integration for the custom integration equivalent

Blueprint Code

Click the badge to import this Blueprint: (needs Home Assistant Core 2021.3 or higher)

Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.

Or import this Blueprint by using the forum topic / Gist URL:

Gist url

blueprint:
  name: MQTT Statestream to MQTT Discovery 2021.03
  description: Creates an MQTT discovery entry for every entity streamed via MQTT Statestream.
  domain: automation
  input:
    ha_stream:
      name: HA stream
      description: The HA statestream topic prefix used

mode: parallel
max: 50

trigger_variables:
  base_topic: !input ha_stream

trigger:
  - platform: mqtt
    topic: "{{ base_topic ~ '/sensor/+/state' }}"
  - platform: mqtt
    topic: "{{ base_topic ~ '/binary_sensor/+/state' }}"
  - platform: mqtt
    topic: "{{ base_topic ~ '/light/+/state' }}"
  - platform: mqtt
    topic: "{{ base_topic ~ '/switch/+/state' }}"
  - platform: mqtt
    topic: "{{ base_topic ~ '/light/+/set' }}"
  - platform: mqtt
    topic: "{{ base_topic ~ '/light/+/set_bri' }}"
  - platform: mqtt
    topic: "{{ base_topic ~ '/switch/+/set' }}"
  
action:
  - variables:
      t: "{{ trigger.topic.split('/') }}"
      root: "{{ t[0] }}"
      domain: "{{ t[1] }}"
      entity: "{{ t[2] }}"
      element: "{{ t[3] }}"

  - choose:
      # Create Sensor discovery config
      - conditions:
          - condition: template
            value_template: "{{ domain == 'sensor' and element == 'state' and 'device_class' in states[domain + '.' + entity].attributes }}"

        sequence:
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/attributes"
              payload: '{
                {% for att in states[domain + "."+ entity].attributes %}
                "{{ att }}": "{{ state_attr(domain + "."+ entity, att) }}",
                {% endfor %}
                "mqttstream2discover": "true"
                }'
              retain: true
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/config"
              payload: '{
                "uniq_id": "mqtt_{{domain }}_{{ entity }}",
                "name": "{{ entity| replace(''_'', '' '') | title }}",
                "dev_cla": "{{ state_attr(domain + ''.'' + entity, "device_class") }}",
                {% if state_attr(domain + ''.'' + entity, "state_class") != none %}
                  "stat_cla": "{{ state_attr(domain + ''.'' + entity, "state_class") }}",
                {% endif %}
                "unit_of_meas": "{{ state_attr(domain + ''.'' + entity, "unit_of_measurement") }}",
                "stat_t": "{{ root }}/{{ domain }}/{{ entity }}/state",
                "json_attr_t": "{{ root }}/{{ domain }}/{{ entity }}/attributes"
                }'
              retain: true

      # Create Binary Sensor discovery config
      - conditions:
          - condition: template
            value_template: "{{ domain == 'binary_sensor' and element == 'state' and 'device_class' in states[domain + '.' + entity].attributes }}"

        sequence:
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/attributes"
              payload: '{
                {% for att in states[domain + "."+ entity].attributes %}
                "{{ att }}": "{{ state_attr(domain + "."+ entity, att) }}",
                {% endfor %}
                "mqttstream2discover": "true"
                }'
              retain: true
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/config"
              payload: '{
                "uniq_id": "mqtt_{{domain }}_{{ entity }}",
                "name": "{{ entity| replace(''_'', '' '') | title }}",
                "dev_cla": "{{ state_attr(domain + ''.'' + entity, "device_class") }}",
                "pl_off":"off", "pl_on":"on",
                "state_topic": "{{ root }}/{{ domain }}/{{ entity }}/state",
                "json_attr_t": "{{ root }}/{{ domain }}/{{ entity }}/attributes"
                }'
              retain: true

      # Create Switch discovery config
      - conditions:
          - condition: template
            value_template: "{{  
              domain == 'switch' and 
              element == 'state' }}"

        sequence:
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/attributes"
              payload: '{
                {% for att in states[domain + "."+ entity].attributes %}
                "{{ att }}": "{{ state_attr(domain + "."+ entity, att) }}",
                {% endfor %}
                "mqttstream2discover": "true"
                }'
              retain: true
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/config"
              payload: '{
                "uniq_id": "mqtt_{{domain }}_{{ entity }}",
                "name": "{{ entity| replace(''_'', '' '') | title }}",
                "pl_off":"off", "pl_on":"on",
                "state_topic": "{{ root }}/{{ domain }}/{{ entity }}/state",
                "json_attr_t": "{{ root }}/{{ domain }}/{{ entity }}/attributes",
                "command_topic": "{{ root }}/{{ domain }}/{{ entity }}/set"
              }'
              retain: true

      # Create Light discovery config
      - conditions:
          - condition: template
            value_template: "{{  
              domain == 'light' and 
              element == 'state' }}"

        sequence:
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/attributes"
              payload: '{
                {% for att in states[domain + "."+ entity].attributes %}
                "{{ att }}": "{{ state_attr(domain + "."+ entity, att) }}",
                {% endfor %}
                "mqttstream2discover": "true"
                }'
              retain: true
          - service: mqtt.publish
            data_template:
              topic: "{{ root }}/{{ domain }}/{{ entity }}/config"
              payload: '{
                "uniq_id": "mqtt_{{domain }}_{{ entity }}",
                "name": "{{ entity| replace(''_'', '' '') | title }}",
                "pl_off":"off", "pl_on":"on",
                "bri_stat_t": "{{ root }}/{{ domain }}/{{ entity }}/brightness",
                "bri_cmd_t": "{{ root }}/{{ domain }}/{{ entity }}/set_bri",
                "state_topic": "{{ root }}/{{ domain }}/{{ entity }}/state",
                "json_attr_t": "{{ root }}/{{ domain }}/{{ entity }}/attributes",
                "command_topic": "{{ root }}/{{ domain }}/{{ entity }}/set"
              }'
              retain: true
      # Command for Switch & Light 
      - conditions:
          - condition: template
            value_template: "{{  
              (domain == 'light' or domain == 'switch') and 
              element == 'set' }}"

        sequence:
          - service: '{{ domain }}.turn_{{trigger.payload | lower }}'
            data_template:
              entity_id: '{{ domain }}.{{ entity }}'

      # Command for Light brightness
      - conditions:
          - condition: template
            value_template: "{{  
              domain == 'light' and 
              element == 'set_bri' }}"

        sequence:
          - service: 'light.turn_on'
            data_template:
              entity_id: '{{ domain }}.{{ entity }}'
              brightness: '{{trigger.payload | int }}'

    default:
      - event: noop
3 Likes

Just a small suggestion to reduce the code size, employ variables.

If you do this:

action:
  - variables:
      t: "{{ trigger.topic.split('/') }}"
  - conditions:
    .. etc ...

then you can refer to the list’s items in the code like this:

topic: "{{ t[0] }}/{{ t[1] }}/{{ t[2] }}/attributes"

instead of this:

topic: "{{ trigger.topic.split('/')[0] }}/{{ trigger.topic.split('/')[1] }}/{{ trigger.topic.split('/')[2] }}/attributes"
1 Like

Great work, thank you!

binary_sensor condition template is referring to “sensor” though, I had to edit the yaml. :slight_smile:

1 Like

Thanks. Fixed

Great work! But I can´t figure out how it should work. My slave-HA holds all my zigbee-lights and the HA-master should controll the lights on the slave. Installed the blueprint on both master and slave but when I change brightness on the master it flipps back and forth. What could I do wrong?

The blueprint should be installed only on the slave, or you’ll have conflicts.
On the slave, you also need to configure mqtt statestream.

On the master, you just have to enable mqtt discovery.

Thanks! Works fine.

One more Q: In my lab config I run the MQTT broker on a dedicated server with “discovery: true” and “discovery_prefix” on my master HA, but in my prod environment I have the MQTT broker installed on my master HA as a Add-On. Do I have to config something special to make it work on my prod master?

Br
//Magnus

The location of your broker doesn’t matter, as long as you have discovery enabled.

I’m interested if you might be adding more domains (types) to this integration ?

I’m asking because I’d like to leverage it from another platform where I wrote an MQTT integration which currently parses statestream and controls back via either installable automations or the API. It would be neat if I could just ask users to install your Blueprint instead.

Aside from some devices having bad config payloads (which I can fix as a pull request I hope) I notice that this sends all topics for a device on every state update which generates huge volumes of updates to MQTT clients. For retained topics this is unnecessary.

The particularly problematic one is that the config message is resent on every state update and that triggers a lot of onward parsing in my app. I don’t really want to unsubscribe from the wildcarded config topics as that would mean I wouldn’t pick up new devices so I am looking to see if there is any way on the HA Blueprint side not to send these extra messages, especially config, except for new devices (or any change to the config payload)

If you have a bright idea to detect, from the blueprint, that the config topic already exists and is up-to-date, I’m all ears, but I don’t.

If the continuous generation of the config is a problem for you, just disable the automation based upon the blueprint once they have been generated once.
OFC, that means automating on your side the fact to re-enable it when new items are added to the “slave”.

This blueprint is now unmaintained.
It was getting too cumbersome to add functionalities with templates alone, so I created a custom component, MQTT DiscoveryStream integration as a continuation of this work.

Thanks