Multiple MQTT topics in one entity

Hi there, using this lib GitHub - plapointe6/EspMQTTClient: Wifi and MQTT handling for ESP8266 and ESP32 with an ESP8266 based board, I succeed in controlling one GPIO from HA with this code :

light 1:    
    - platform: "mqtt"
      name: "test esp"
      unique_id: "test_esp"
      state_topic: "test_esp/state"
      command_topic: "test_esp/cmd1"
      payload_on: "1"
      payload_off: "0"
      qos: 0
      retain: true

But the ESP8266 board can handle many more GPIO, and I want to be able to use them, but I can’t figure out the code on the HA side.
On the ESP side, I can subscribe to or publish to any topics I want (like test_esp/cmd1, test_esp/cmd2 …) , but how can I group them in one entity or device in HA ? I had a look in groups but I don’t think this is the right way. Thanks for your help !

I’m not familiar with the library you’re using (ESPHome handles this I think) but take a look at MQTT Discovery-related docs in HA. If you specify the same device payload for multiple entities the entities are all grouped with that “device” in HA configuration. Then I think you could create 1 entity per gpio or sensor/switch/whatever hooked up to your esp8266/32.

Either you could configure your device to send the discovery payload per entity at startup (ideally) or you can construct one manually and send it once per entity by hand. I think you can use an HA service to publish the MQTT message or any other MQTT tooling.

This would let you/require you to remove the yaml-based mqtt entity definitions from your HA config.

Thanks for your reply, this is a good idea. The lib I am using supports MQTT discovery via GitHub - plapointe6/HAMqttDevice: A library that provides tools to simplify MQTT discovery implementation for Home Assistant to your device. The thing is I moved away from MQTT discovery because unwanted and / or random commands were sent to my others MQTT devices and I ended up writing the whole configuration.yaml file manually. But I might consider it again … if I get some time for it

So I am getting on with this, and I almost succeeded in registering more than one MQTT topic. The thing is HA “sees” only one entity (“auto-discovery-light-1” in my example) but it stops here and the other one “auto-discovery-light-2” never shows up in HA entities panel.
This is strange because using a tool like MQTTfx I can see that this topic is properly registered with Mosquitto and I even can send / receive commands to / from it like ha/light/auto-discovery-light-2/cmd/ON
So the part I can’t figure out is what to do on HA side to get the second entity showing.
Fore reference, here is the code I use on the ESP8266 side (it’s a bit long, but easily readeable)

#include <Arduino.h>
#include "EspMQTTClient.h"
#include "HAMqttDevice.h"
#include <ESP_EEPROM.h>

// MQTT client setup
EspMQTTClient client(
    "************",      // Wifi ssid
    "************",      // Wifi password
    "************",      // MQTT broker ip
    "************",      // MQTT username
    "************",      // MQTT password
    "auto_discovery_test_lights" // MQTT Client name
);

// Home Assitant device creation for MQTT discovery
HAMqttDevice light1("auto-discovery-light-1", HAMqttDevice::LIGHT);
HAMqttDevice light2("auto-discovery-light-2", HAMqttDevice::LIGHT);

// Pin assignation
const int light1Pin = D0;
const int light2Pin = D1;

// Current status of the light
struct LightValues
{
  bool light1On;
  bool light2On;
};
struct LightValues lightValues = {false, false};

void setup()
{
  Serial.begin(115200);

  pinMode(light1Pin, OUTPUT);
  pinMode(light2Pin, OUTPUT);

  // Charge saved values from eeprom
  EEPROM.begin(sizeof(lightValues));
  if (EEPROM.percentUsed() >= 0)
    EEPROM.get(0, lightValues);

  // Setup the parameters of the device for MQTT discovery in HA
  light1.enableAttributesTopic();
  light2.enableAttributesTopic();

  client.enableHTTPWebUpdater();
  client.setMaxPacketSize(512);
  client.enableDebuggingMessages();
}

void onConnectionEstablished()
{
  // Subscribe to the command topic ("cmd_t") of this device for light1
  client.subscribe(light1.getCommandTopic(), [](const String &payload) {
    // Turn the light on/off depending on the command received from Home Assitant
    if (payload.equals("ON"))
      lightValues.light1On = true;
    else if (payload.equals("OFF"))
      lightValues.light1On = false;

    // Confirm to Home Assitant that we received the command and updated the state.
    // HA will then update the state of the device in HA
    client.publish(light1.getStateTopic(), payload);

    // Save the new state in memory
    EEPROM.put(0, lightValues.light1On);
    EEPROM.commit();
  });

  // Subscribe to the command topic ("cmd_t") of this device for light2
  client.subscribe(light2.getCommandTopic(), [](const String &payload) {
    // Turn the light on/off depending on the command received from Home Assitant
    if (payload.equals("ON"))
      lightValues.light2On = true;
    else if (payload.equals("OFF"))
      lightValues.light2On = false;

    // Confirm to Home Assitant that we received the command and updated the state.
    // HA will then update the state of the device in HA
    client.publish(light2.getStateTopic(), payload);

    // Save the new state in memory
    EEPROM.put(0, lightValues.light2On);
    EEPROM.commit();
  });

  // Subscribe to Home Assitant connection status events.
  client.subscribe("ha/status", [](const String &payload) {
    // When the status of Home assistant get online, publish the device config
    if (payload.equals("online"))
    {
      client.publish(light1.getConfigTopic(), light1.getConfigPayload());
      client.publish(light2.getConfigTopic(), light2.getConfigPayload());

      // After sending the device config to HA, wait a little to allow HA to create the entity.
      client.executeDelayed(5 * 1000, []() {
        // Send the current state of the light
        client.publish(light1.getStateTopic(), lightValues.light1On ? "ON" : "OFF");
        client.publish(light2.getStateTopic(), lightValues.light2On ? "ON" : "OFF");

        // Send the IP address of the device to HA
        light1
            .clearAttributes()
            .addAttribute("IP", WiFi.localIP().toString());

        light2
            .clearAttributes()
            .addAttribute("IP", WiFi.localIP().toString());

        client.publish(light1.getAttributesTopic(), light1.getAttributesPayload());
        client.publish(light2.getAttributesTopic(), light2.getAttributesPayload());
      });
    }
  });
}

void loop()
{
  client.loop();

  if (lightValues.light1On)
    digitalWrite(light1Pin, LOW);
  else
    digitalWrite(light1Pin, HIGH);

  if (lightValues.light2On)
    digitalWrite(light2Pin, LOW);
  else
    digitalWrite(light2Pin, HIGH);
}

I just had a look at the logs and this is what I found :

2021-04-21 12:01:27 DEBUG (MainThread) [homeassistant.components.mqtt] Received message on ha/light/auto-discovery-light-1/config: b'{"~":"ha/light/auto-discovery-light-1","name":"auto-discovery-light-1","cmd_t":"~/cmd","stat_t":"~/state","json_attr_t":"~/attr"}'
2021-04-21 12:01:27 DEBUG (MainThread) [homeassistant.components.mqtt.discovery] Process discovery payload {'name': 'auto-discovery-light-1', 'command_topic': 'ha/light/auto-discovery-light-1/cmd', 'state_topic': 'ha/light/auto-discovery-light-1/state', 'json_attributes_topic': 'ha/light/auto-discovery-light-1/attr', 'platform': 'mqtt'}
2021-04-21 12:01:27 INFO (MainThread) [homeassistant.components.mqtt.discovery] Component has already been discovered: light auto-discovery-light-1, sending update
2021-04-21 12:01:27 INFO (MainThread) [homeassistant.components.mqtt.mixins] Got update for entity with hash: ('light', 'auto-discovery-light-1') '{'name': 'auto-discovery-light-1', 'command_topic': 'ha/light/auto-discovery-light-1/cmd', 'state_topic': 'ha/light/auto-discovery-light-1/state', 'json_attributes_topic': 'ha/light/auto-discovery-light-1/attr', 'platform': 'mqtt'}'
2021-04-21 12:01:27 INFO (MainThread) [homeassistant.components.mqtt.mixins] Ignoring unchanged update for: light.auto_discovery_light_1
2021-04-21 12:01:27 DEBUG (MainThread) [homeassistant.components.mqtt.discovery] Pending discovery for ('light', 'auto-discovery-light-1'): deque([])
2021-04-21 12:01:27 DEBUG (MainThread) [homeassistant.components.mqtt] Received message on ha/light/auto-discovery-light-2/config: b'{"~":"ha/light/auto-discovery-light-2","name":"auto-discovery-light-2"}'
2021-04-21 12:01:27 DEBUG (MainThread) [homeassistant.components.mqtt.discovery] Process discovery payload {'name': 'auto-discovery-light-2', 'platform': 'mqtt'}
2021-04-21 12:01:27 INFO (MainThread) [homeassistant.components.mqtt.discovery] Found new component: light auto-discovery-light-2
2021-04-21 12:01:27 ERROR (MainThread) [homeassistant.util.logging] Exception in async_discover when dispatching 'mqtt_discovery_new_light_mqtt': ({'name': 'auto-discovery-light-2', 'platform': 'mqtt'},)
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/mqtt/mixins.py", line 160, in async_discover
    config = schema(discovery_payload)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/validators.py", line 218, in __call__
    return self._exec((Schema(val) for val in self.validators), v)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/validators.py", line 341, in _exec
    raise e if self.msg is None else AllInvalid(self.msg, path=path)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/validators.py", line 337, in _exec
    v = func(v)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/schema_builder.py", line 272, in __call__
    return self._compiled([], data)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/schema_builder.py", line 817, in validate_callable
    return schema(data)
  File "/usr/src/homeassistant/homeassistant/components/mqtt/light/__init__.py", line 26, in validate_mqtt_light
    return schemas[value[CONF_SCHEMA]](value)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/schema_builder.py", line 272, in __call__
    return self._compiled([], data)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/schema_builder.py", line 594, in validate_dict
    return base_validate(path, iteritems(data), out)
  File "/usr/local/lib/python3.8/site-packages/voluptuous/schema_builder.py", line 432, in validate_mapping
    raise er.MultipleInvalid(errors)
voluptuous.error.MultipleInvalid: required key not provided @ data['command_topic']
2021-04-21 12:01:27 DEBUG (MainThread) [homeassistant.components.mqtt.discovery] Pending discovery for ('light', 'auto-discovery-light-2'): deque([])
2021-04-21 12:01:32 DEBUG (MainThread) [homeassistant.components.mqtt] Received message on ha/light/auto-discovery-light-1/state: b'OFF'
2021-04-21 12:01:32 DEBUG (MainThread) [homeassistant.components.mqtt] Received message on ha/light/auto-discovery-light-1/attr: b'{"IP":"192.168.10.64"}'

HA raises an error regarding the discovery of the 2nd topic :

2021-04-21 12:01:27 ERROR (MainThread) [homeassistant.util.logging] Exception in async_discover when dispatching 'mqtt_discovery_new_light_mqtt': ({'name': 'auto-discovery-light-2', 'platform': 'mqtt'},)