Grouping MQTT sensors into devices with ArduinoJson and PubSubClient

Hello,

I built some custom Ethernet sensors using Raspberry Pi Picos programmed with Arduino IDE. I can successfully turn get the sensors discovered by Home Assistant as entities, but would like them to be grouped into devices too. I use the ArduinoJson library to serialize the discovery and state topics and then send them via MQTT using PubSubClient library. Does anyone have experience grouping sensors as MQTT devices with JSON and /or these Arduino libraries? I tried looking at the proper HA syntax for JSON device data, but could not find much official documentation. N.B. I don’t want to create devices in configuration.yaml, I would like the auto-discovery to do its thing.

So the following code works to create sensors:

  char buffer[256];
  DynamicJsonDocument doc(1024);

  doc["name"] = "Bedroom Temperature";
  doc["unique_id"] = "temp_bedroom"; 
  doc["device_class"]   = "temperature";
  doc["unit_of_measurement"]   = "°C";  
  doc["state_topic"] = "homeassistant/sensor/temp_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/temp_bedroom/config", buffer);

But the following does not seem to work to create them as part of a device:

  char buffer[256];
  DynamicJsonDocument doc(1024);
  JsonObject device  = doc.createNestedObject("device");

  doc["name"] = "Bedroom Temperature";
  doc["unique_id"] = "temp_bedroom"; 
  doc["device_class"]   = "temperature";
  doc["unit_of_measurement"]   = "°C";  
  doc["state_topic"] = "homeassistant/sensor/temp_bedroom/state";
  device["model"] = "InterMon_Mk1";
  device["identifiers"] = "(homeassistant, IM01)";
  device["name"] = "Bedroom Monitor";   
  device["manufacturer"] = "Lord_VABA"; 
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/temp_bedroom/config", buffer);

If anyone can help with this somewhat exotic matter, I would be much obliged.

What you have looks reasonable…
What does the output message look like in mqtt-Explorer or similar.

Some examples in the below link for working with discovery messages.

@HasQT Thank you for the links. They were quite useful. I also forgot to use explorer for mqtt debugging. The ArduinoJson library website has a useful tool for converting desired Json to Arduino code: https://arduinojson.org/v6/assistant/#/step1

I now serialize the JSON correctly, but PubSubCLient does not seem to publish it. Not sure why.

  char buffer[512];
  DynamicJsonDocument doc(1024);

  doc["name"] = "Bedroom Temperature";
  doc["unique_id"] = "temp_bedroom"; 
  doc["device_class"]   = "temperature";
  doc["unit_of_measurement"]   = "C";  
  doc["state_topic"] = "homeassistant/sensor/temp_bedroom/state";
  JsonObject device  = doc.createNestedObject("device");
  device["identifiers"][0] = "Bedroom_Monitor";
  device["model"] = "InterMon_Mk1";
  device["name"] = "Bedroom Monitor";   
  device["manufacturer"] = "Lord_VABA"; 
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/temp_bedroom/config", buffer);

If I print serialized doc to the Arduino serial monitor, I get:

{"name":"Bedroom Temperature","unique_id":"temp_bedroom","device_class":"temperature","unit_of_measurement":"C","state_topic":"homeassistant/sensor/temp_bedroom/state","device":{"identifiers":["Bedroom_Monitor"],"model":"InterMon_Mk1","name":"Bedroom Monitor","manufacturer":"Lord_VABA"}}

But when I serialize to buffer and try to publish the buffer to mqtt nothing is received by explorer. But it was working fine before I added the nested “device” part.

When I put the above json into the arduino json converter you posted earlier, it provides the following

// Stream& input;

StaticJsonDocument<384> doc;

DeserializationError error = deserializeJson(doc, input);

if (error) {
  Serial.print(F("deserializeJson() failed: "));
  Serial.println(error.f_str());
  return;
}

const char* name = doc["name"]; // "Bedroom Temperature"
const char* unique_id = doc["unique_id"]; // "temp_bedroom"
const char* device_class = doc["device_class"]; // "temperature"
const char* unit_of_measurement = doc["unit_of_measurement"]; // "C"
const char* state_topic = doc["state_topic"]; // "homeassistant/sensor/temp_bedroom/state"

JsonObject device = doc["device"];

const char* device_identifiers_0 = device["identifiers"][0]; // "Bedroom_Monitor"

const char* device_model = device["model"]; // "InterMon_Mk1"
const char* device_name = device["name"]; // "Bedroom Monitor"
const char* device_manufacturer = device["manufacturer"]; // "Lord_VABA"

So I wonder if perhaps

JsonObject device  = doc.createNestedObject("device");

Should be this. I don’t really know though, I don’t really use Arduino for this.

JsonObject device = doc["device"];

Is there no error message from the arduino…?

It is possible that I pasted it wrong. But the actual problem was that the PubSubClient library has a 256 byte buffer size by default. By adding myClient.setBufferSize(512) right after myClient.connect(....) got it to publish the message.

Interestingly it automatically added others sensors to the device. I thought I would have to add the “device” json stack to every sensor, but it seems it just had to be the first one. Not sure how that works. So the current, seemingly working, Arduino code is:

  char buffer[512];
  DynamicJsonDocument doc(1024);

  doc["name"] = "Bedroom Temperature";
  doc["unique_id"] = "temp_bedroom"; 
  doc["device_class"]   = "temperature";
  doc["state_class"]   = "measurement";
  doc["unit_of_measurement"]   = "C";  
  doc["state_topic"] = "homeassistant/sensor/temp_bedroom/state";
  JsonObject device  = doc.createNestedObject("device");
  device["identifiers"][0] = "Bedroom_Monitor";
  device["model"] = "InterMon_Mk1";
  device["name"] = "Bedroom Monitor";   
  device["manufacturer"] = "Lord_VABA"; 
  device["sw_version"] = "1.0";  
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/temp_bedroom/config", buffer);

  doc["name"] = "Bedroom Humidity";
  doc["unique_id"] = "humi_bedroom"; 
  doc["device_class"]   = "humidity";
  doc["unit_of_measurement"]   = "%";  
  doc["state_topic"] = "homeassistant/sensor/humi_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/humi_bedroom/config", buffer);

  doc["name"] = "Bedroom Pressure";
  doc["unique_id"] = "pres_bedroom"; 
  doc["device_class"]   = "pressure";
  doc["unit_of_measurement"]   = "hPa";  
  doc["state_topic"] = "homeassistant/sensor/pres_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/pres_bedroom/config", buffer);

  doc["name"] = "Bedroom CO_2";
  doc["unique_id"] = "co_2_bedroom"; 
  doc["device_class"]   = "carbon_dioxide";
  doc["unit_of_measurement"]   = "ppm";  
  doc["state_topic"] = "homeassistant/sensor/co_2_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/co_2_bedroom/config", buffer);

  doc["name"] = "Bedroom TVOC";
  doc["unique_id"] = "tvoc_bedroom"; 
  doc["device_class"]   = "volatile_organic_compounds";
  doc["unit_of_measurement"]   = "ppb";    
  doc["state_topic"] = "homeassistant/sensor/tvoc_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/tvoc_bedroom/config", buffer);

  doc["name"] = "Bedroom Illuminance";
  doc["unique_id"] = "lumi_bedroom"; 
  doc["device_class"]   = "illuminance";
  doc["unit_of_measurement"]   = "lux";    
  doc["state_topic"] = "homeassistant/sensor/lumi_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/sensor/lumi_bedroom/config", buffer);

  doc["name"] = "Bedroom Motion";
  doc["unique_id"] = "moti_bedroom"; 
  doc["device_class"]   = "motion";
  doc["state_topic"] = "homeassistant/binary_sensor/moti_bedroom/state";
  serializeJson(doc, buffer);
  mqttClient.publish("homeassistant/binary_sensor/moti_bedroom/config", buffer);

Giving the desired result:

So thank you for showing examples of the correct json formatting @HasQT. I shall monitor for bugs from here on. Finally some closure to this at 2AM. I don’t know why this stuff is so addictive.

3 Likes