mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
fixes #2579
This commit is contained in:
@@ -631,7 +631,7 @@ void AnalogSensor::publish_values(const bool force) {
|
|||||||
snprintf(topic, sizeof(topic), "switch/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
snprintf(topic, sizeof(topic), "switch/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
||||||
config["cmd_t"] = command_topic;
|
config["cmd_t"] = command_topic;
|
||||||
Mqtt::add_ha_bool(config);
|
Mqtt::add_ha_bool(config.as<JsonObject>());
|
||||||
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { // DAC
|
} else if (sensor.type() == AnalogType::DIGITAL_OUT) { // DAC
|
||||||
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
snprintf(topic, sizeof(topic), "number/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(analogsensor), sensor.name().c_str());
|
||||||
@@ -665,7 +665,7 @@ void AnalogSensor::publish_values(const bool force) {
|
|||||||
// config["step"] = sensor.factor();
|
// config["step"] = sensor.factor();
|
||||||
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
} else if (sensor.type() == AnalogType::DIGITAL_IN) {
|
||||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
snprintf(topic, sizeof(topic), "binary_sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||||
Mqtt::add_ha_bool(config);
|
Mqtt::add_ha_bool(config.as<JsonObject>());
|
||||||
} else {
|
} else {
|
||||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
snprintf(topic, sizeof(topic), "sensor/%s/%s_%02d/config", Mqtt::basename().c_str(), F_(analogsensor), sensor.gpio());
|
||||||
config["stat_cla"] = "measurement";
|
config["stat_cla"] = "measurement";
|
||||||
@@ -680,7 +680,8 @@ void AnalogSensor::publish_values(const bool force) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mqtt::add_ha_sections_to_doc("analog", stat_t, config, !is_ha_device_created, val_cond);
|
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Analog Sensors", nullptr, nullptr, false);
|
||||||
|
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !is_ha_device_created, val_cond);
|
||||||
|
|
||||||
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
sensor.ha_registered = Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1221,7 +1221,7 @@ void EMSdevice::setCustomizationEntity(const std::string & entity_id) {
|
|||||||
if (Mqtt::ha_enabled() && (has_custom_name || ((current_mask ^ new_mask) & (DeviceValueState::DV_READONLY >> 4)))) {
|
if (Mqtt::ha_enabled() && (has_custom_name || ((current_mask ^ new_mask) & (DeviceValueState::DV_READONLY >> 4)))) {
|
||||||
// remove ha config on change of dv_readonly flag
|
// remove ha config on change of dv_readonly flag
|
||||||
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
dv.remove_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||||
Mqtt::publish_ha_sensor_config(dv, "", "", true); // delete topic (remove = true)
|
Mqtt::publish_ha_sensor_config_dv(dv, "", "", true); // delete topic (remove = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// always write the mask
|
// always write the mask
|
||||||
@@ -1673,7 +1673,7 @@ void EMSdevice::get_value_json(JsonObject json, DeviceValue & dv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add uom, state class and device class
|
// add uom, state class and device class
|
||||||
Mqtt::add_ha_uom(json, dv.type, dv.uom, dv.short_name, false); // no icon
|
Mqtt::add_ha_classes(json, device_type(), dv.type, dv.uom, dv.short_name, false); // no icon
|
||||||
|
|
||||||
json["readable"] = dv.type != DeviceValueType::CMD && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE);
|
json["readable"] = dv.type != DeviceValueType::CMD && !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE);
|
||||||
json["writeable"] = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY);
|
json["writeable"] = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY);
|
||||||
@@ -1886,7 +1886,7 @@ void EMSdevice::mqtt_ha_entity_config_create() {
|
|||||||
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
if (!dv.has_state(DeviceValueState::DV_HA_CONFIG_CREATED) && dv.has_state(DeviceValueState::DV_ACTIVE)
|
||||||
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
&& !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE)) {
|
||||||
// create_device_config is only done once for the EMS device. It can added to any entity, so we take the first
|
// create_device_config is only done once for the EMS device. It can added to any entity, so we take the first
|
||||||
if (Mqtt::publish_ha_sensor_config(dv, name().c_str(), brand_to_char(), false, create_device_config)) {
|
if (Mqtt::publish_ha_sensor_config_dv(dv, name().c_str(), brand_to_char(), false, create_device_config)) {
|
||||||
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
dv.add_state(DeviceValueState::DV_HA_CONFIG_CREATED);
|
||||||
create_device_config = false; // only create the main config once
|
create_device_config = false; // only create the main config once
|
||||||
count++;
|
count++;
|
||||||
|
|||||||
@@ -744,39 +744,17 @@ bool Mqtt::queue_ha(const char * topic, const JsonObjectConst payload) {
|
|||||||
return queue_publish_message(Mqtt::discovery_prefix() + topic, payload_text, true); // with retain true
|
return queue_publish_message(Mqtt::discovery_prefix() + topic, payload_text, true); // with retain true
|
||||||
}
|
}
|
||||||
|
|
||||||
// create's a ha sensor config topic from a device value object
|
// create's a ha sensor config topic from a device value object (dev)
|
||||||
|
// adds ids, name, mf, mdl, via_device
|
||||||
// and also takes a flag (create_device_config) used to also create the main HA device config. This is only needed for one entity
|
// and also takes a flag (create_device_config) used to also create the main HA device config. This is only needed for one entity
|
||||||
bool Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config) {
|
bool Mqtt::publish_ha_sensor_config_dv(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config) {
|
||||||
JsonDocument dev_json;
|
|
||||||
|
|
||||||
// always create the ids (discovery identifiers)
|
|
||||||
// with the name always
|
|
||||||
// and the manufacturer and model if we're creating the device config for the first entity
|
|
||||||
JsonArray ids = dev_json["ids"].to<JsonArray>();
|
|
||||||
char ha_device[40];
|
|
||||||
auto device_type_name = EMSdevice::device_type_2_device_name(dv.device_type);
|
|
||||||
snprintf(ha_device, sizeof(ha_device), "%s-%s", Mqtt::basename().c_str(), device_type_name);
|
|
||||||
ids.add(ha_device);
|
|
||||||
|
|
||||||
auto cap_name = strdup(device_type_name);
|
|
||||||
Helpers::CharToUpperUTF8(cap_name); // capitalize first letter
|
|
||||||
dev_json["name"] = Mqtt::basename() + " " + cap_name;
|
|
||||||
free(cap_name);
|
|
||||||
|
|
||||||
// create only once per category
|
|
||||||
if (create_device_config) {
|
|
||||||
dev_json["mf"] = brand;
|
|
||||||
dev_json["mdl"] = model;
|
|
||||||
dev_json["via_device"] = Mqtt::basename();
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate the min and max
|
// calculate the min and max
|
||||||
int16_t dv_set_min;
|
int16_t dv_set_min;
|
||||||
uint32_t dv_set_max;
|
uint32_t dv_set_max;
|
||||||
(void)dv.get_min_max(dv_set_min, dv_set_max);
|
(void)dv.get_min_max(dv_set_min, dv_set_max);
|
||||||
|
|
||||||
// determine if we're creating the command topics which we use special HA configs
|
// determine if we're creating the command topics which we use special HA configs
|
||||||
// unless the entity has been marked as read-only and so it'll default to using the sensor/ type
|
// if the entity has been marked as read-only it'll be created as a binary_sensor or sensor
|
||||||
// or we're dealing with Energy sensors that must have "diagnostic" as an entity category (e.g. negheat & nrgww)
|
// or we're dealing with Energy sensors that must have "diagnostic" as an entity category (e.g. negheat & nrgww)
|
||||||
bool has_cmd = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY) && (dv.uom != DeviceValueUOM::KWH);
|
bool has_cmd = dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY) && (dv.uom != DeviceValueUOM::KWH);
|
||||||
|
|
||||||
@@ -794,20 +772,14 @@ bool Mqtt::publish_ha_sensor_config(DeviceValue & dv, const char * model, const
|
|||||||
dv_set_min,
|
dv_set_min,
|
||||||
dv_set_max,
|
dv_set_max,
|
||||||
dv.numeric_operator,
|
dv.numeric_operator,
|
||||||
dev_json.as<JsonObject>());
|
model,
|
||||||
|
brand,
|
||||||
|
create_device_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// publish HA sensor for System using the heartbeat tag
|
// publish HA sensor specific for System using the heartbeat tag
|
||||||
bool Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom) {
|
bool Mqtt::publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom) {
|
||||||
JsonDocument doc;
|
return publish_ha_sensor_config(type, DeviceValueTAG::TAG_DEVICE_DATA, name, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, 0);
|
||||||
JsonObject dev_json = doc["dev"].to<JsonObject>();
|
|
||||||
|
|
||||||
dev_json["name"] = Mqtt::basename();
|
|
||||||
JsonArray ids = dev_json["ids"].to<JsonArray>();
|
|
||||||
ids.add(Mqtt::basename());
|
|
||||||
|
|
||||||
return publish_ha_sensor_config(
|
|
||||||
type, DeviceValueTAG::TAG_DEVICE_DATA, name, name, EMSdevice::DeviceType::SYSTEM, entity, uom, false, false, nullptr, 0, 0, 0, 0, dev_json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MQTT discovery configs
|
// MQTT discovery configs
|
||||||
@@ -827,7 +799,9 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
const int16_t dv_set_min,
|
const int16_t dv_set_min,
|
||||||
const uint32_t dv_set_max,
|
const uint32_t dv_set_max,
|
||||||
const int8_t num_op,
|
const int8_t num_op,
|
||||||
const JsonObjectConst dev_json) {
|
const char * const model,
|
||||||
|
const char * const brand,
|
||||||
|
const bool create_device_config) {
|
||||||
// ignore if name (fullname) is empty
|
// ignore if name (fullname) is empty
|
||||||
if (!fullname || !en_name) {
|
if (!fullname || !en_name) {
|
||||||
return false;
|
return false;
|
||||||
@@ -984,8 +958,10 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if at this point we don't have a topic created yet, create a default sensor one. We always need a topic.
|
// if at this point we don't have a topic created yet, create a default sensor one. We always need a topic.
|
||||||
|
bool is_sensor = false;
|
||||||
if (!strnlen(topic, sizeof(topic))) {
|
if (!strnlen(topic, sizeof(topic))) {
|
||||||
snprintf(topic, sizeof(topic), (type == DeviceValueType::BOOL) ? "binary_sensor/%s" : "sensor/%s", config_topic); // binary sensor (for booleans)
|
snprintf(topic, sizeof(topic), (type == DeviceValueType::BOOL) ? "binary_sensor/%s" : "sensor/%s", config_topic); // binary sensor (for booleans)
|
||||||
|
is_sensor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're asking to remove this topic, send an empty payload and exit
|
// if we're asking to remove this topic, send an empty payload and exit
|
||||||
@@ -995,7 +971,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
return queue_remove_topic(topic);
|
return queue_remove_topic(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
// build the payload
|
// build the full payload
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
doc["uniq_id"] = uniq_id;
|
doc["uniq_id"] = uniq_id;
|
||||||
doc["obj_id"] = uniq_id; // same as unique_id
|
doc["obj_id"] = uniq_id; // same as unique_id
|
||||||
@@ -1065,8 +1041,8 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
free(F_name); // very important!
|
free(F_name); // very important!
|
||||||
doc["name"] = ha_name;
|
doc["name"] = ha_name;
|
||||||
|
|
||||||
// not needed for commands
|
// add state_topic and it's value_template. This is not needed for commands, only sensors
|
||||||
if (type != DeviceValueType::CMD) {
|
if (type != DeviceValueType::CMD || is_sensor) {
|
||||||
// state topic, except for commands
|
// state topic, except for commands
|
||||||
char stat_t[MQTT_TOPIC_MAX_SIZE];
|
char stat_t[MQTT_TOPIC_MAX_SIZE];
|
||||||
|
|
||||||
@@ -1095,7 +1071,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||||
// has no unit of measure or icon, and must be true/false (not on/off or 1/0)
|
// has no unit of measure or icon, and must be true/false (not on/off or 1/0)
|
||||||
if (type == DeviceValueType::BOOL) {
|
if (type == DeviceValueType::BOOL) {
|
||||||
add_ha_bool(doc);
|
add_ha_bool(doc.as<JsonObject>());
|
||||||
strlcpy(sample_val, "false", sizeof(sample_val)); // default is "false"
|
strlcpy(sample_val, "false", sizeof(sample_val)); // default is "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1105,7 +1081,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
|
|
||||||
// adds availability, dev, ids to the config section to HA Discovery config
|
// adds availability, dev, ids to the config section to HA Discovery config
|
||||||
// except for commands
|
// except for commands
|
||||||
add_ha_sections_to_doc(nullptr, stat_t, doc, false, val_cond); // no name, since the "dev" has already been added
|
add_ha_avail_section(doc.as<JsonObject>(), stat_t, false, val_cond);
|
||||||
} else {
|
} else {
|
||||||
// Domoticz doesn't support value templates, so we just use the value directly
|
// Domoticz doesn't support value templates, so we just use the value directly
|
||||||
// Also omit the uom and other state classes
|
// Also omit the uom and other state classes
|
||||||
@@ -1116,18 +1092,24 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev
|
|||||||
// Add the state class, device class and an optional icon based on the uom
|
// Add the state class, device class and an optional icon based on the uom
|
||||||
// first set the catagory for System entities
|
// first set the catagory for System entities
|
||||||
// https://github.com/emsesp/EMS-ESP32/discussions/1459#discussioncomment-7694873
|
// https://github.com/emsesp/EMS-ESP32/discussions/1459#discussioncomment-7694873
|
||||||
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
add_ha_classes(doc.as<JsonObject>(), device_type, type, uom, entity);
|
||||||
doc["ent_cat"] = "diagnostic"; // instead of config
|
|
||||||
}
|
|
||||||
add_ha_uom(doc.as<JsonObject>(), type, uom, entity);
|
|
||||||
|
|
||||||
doc["dev"] = dev_json;
|
// add dev section
|
||||||
|
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||||
|
add_ha_dev_section(doc.as<JsonObject>(), nullptr, nullptr, nullptr, false);
|
||||||
|
} else {
|
||||||
|
add_ha_dev_section(doc.as<JsonObject>(), EMSdevice::device_type_2_device_name(device_type), model, brand, create_device_config);
|
||||||
|
}
|
||||||
|
|
||||||
return queue_ha(topic, doc.as<JsonObject>());
|
return queue_ha(topic, doc.as<JsonObject>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the uom, state class, device class and an optional icon based on the uom
|
// Add the uom, state class, device class and an optional icon based on the uom
|
||||||
void Mqtt::add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, const char * entity, bool is_discovery) {
|
void Mqtt::add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8_t type, const uint8_t uom, const char * entity, bool is_discovery) {
|
||||||
|
if (device_type == EMSdevice::DeviceType::SYSTEM) {
|
||||||
|
doc["ent_cat"] = "diagnostic"; // instead of 'config'
|
||||||
|
}
|
||||||
|
|
||||||
// for HA discovery we use different namings
|
// for HA discovery we use different namings
|
||||||
const char * dc_ha = is_discovery ? "dev_cla" : "device_class"; // device class
|
const char * dc_ha = is_discovery ? "dev_cla" : "device_class"; // device class
|
||||||
const char * sc_ha = is_discovery ? "stat_cla" : "state_class"; // state class
|
const char * sc_ha = is_discovery ? "stat_cla" : "state_class"; // state class
|
||||||
@@ -1145,8 +1127,7 @@ void Mqtt::add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, con
|
|||||||
} else if (uom != DeviceValueUOM::NONE) {
|
} else if (uom != DeviceValueUOM::NONE) {
|
||||||
doc[uom_ha] = EMSdevice::uom_to_string(uom); // use default
|
doc[uom_ha] = EMSdevice::uom_to_string(uom); // use default
|
||||||
} else if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
} else if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
||||||
// Domoticz use " " for a no-uom
|
doc[uom_ha] = " "; // Domoticz uses " " for a no-uom
|
||||||
doc[uom_ha] = " ";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1353,8 +1334,8 @@ bool Mqtt::publish_ha_climate_config(const int8_t tag, const bool has_roomtemp,
|
|||||||
modes.add("heat");
|
modes.add("heat");
|
||||||
modes.add("off");
|
modes.add("off");
|
||||||
|
|
||||||
// device name must be different to the entity name, take the ids value we just created
|
add_ha_dev_section(doc.as<JsonObject>(), "thermostat", nullptr, nullptr, false); // add dev section
|
||||||
add_ha_sections_to_doc("thermostat", topic_t, doc, false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond);
|
add_ha_avail_section(doc.as<JsonObject>(), topic_t, false, seltemp_cond, has_roomtemp ? currtemp_cond : nullptr, hc_mode_cond); // add availability section
|
||||||
|
|
||||||
return queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
return queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
}
|
}
|
||||||
@@ -1378,34 +1359,52 @@ std::string Mqtt::tag_to_topic(uint8_t device_type, int8_t tag) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds availability, dev, ids to the config section to HA Discovery config
|
// add devs section to an existing doc, only for HA
|
||||||
void Mqtt::add_ha_sections_to_doc(const char * name,
|
// under devs node it will create ids and name and optional mf, mdl, via_device
|
||||||
const char * state_t,
|
// name could be EMSdevice::device_type_2_device_name(dv.device_type));
|
||||||
JsonDocument & config,
|
void Mqtt::add_ha_dev_section(JsonObject doc, const char * name, const char * model, const char * brand, const bool create_model) {
|
||||||
const bool is_first,
|
// only works for HA
|
||||||
const char * cond1,
|
|
||||||
const char * cond2,
|
|
||||||
const char * negcond) {
|
|
||||||
// only for HA
|
|
||||||
if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// adds dev section to HA Discovery config
|
// create dev section
|
||||||
|
JsonObject dev_json = doc["dev"].to<JsonObject>();
|
||||||
|
|
||||||
|
// add ids and name - with capitalize first letter
|
||||||
|
JsonArray ids = dev_json["ids"].to<JsonArray>(); // ids, it is an array with a single element
|
||||||
if (name != nullptr) {
|
if (name != nullptr) {
|
||||||
JsonObject dev = config["dev"].to<JsonObject>();
|
// for ids, replace all spaces with -
|
||||||
char * cap_name = strdup(name);
|
std::string lower_name_str(name);
|
||||||
cap_name[0] = toupper(name[0]); // capitalize first letter
|
std::replace(lower_name_str.begin(), lower_name_str.end(), ' ', '-');
|
||||||
dev["name"] = std::string(Mqtt::basename()) + " " + cap_name;
|
ids.add(Mqtt::basename() + "-" + Helpers::toLower(lower_name_str));
|
||||||
// if it's the first in the category, attach the group to the main HA device
|
|
||||||
if (is_first) {
|
auto cap_name = strdup(name);
|
||||||
dev["mf"] = "EMS-ESP";
|
Helpers::CharToUpperUTF8(cap_name); // capitalize first letter
|
||||||
dev["mdl"] = cap_name;
|
dev_json["name"] = Mqtt::basename() + " " + cap_name;
|
||||||
dev["via_device"] = Mqtt::basename();
|
|
||||||
}
|
|
||||||
JsonArray ids = dev["ids"].to<JsonArray>();
|
|
||||||
ids.add(Mqtt::basename() + "-" + Helpers::toLower(name));
|
|
||||||
free(cap_name);
|
free(cap_name);
|
||||||
|
} else {
|
||||||
|
ids.add(Mqtt::basename());
|
||||||
|
dev_json["name"] = Mqtt::basename();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is used to only create it once when entities are dynamically added
|
||||||
|
if (create_model) {
|
||||||
|
dev_json["mf"] = brand != nullptr ? brand : "EMS-ESP";
|
||||||
|
if (model != nullptr) {
|
||||||
|
dev_json["mdl"] = model;
|
||||||
|
}
|
||||||
|
dev_json["via_device"] = Mqtt::basename();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds sections for HA Discovery to an existing JSON doc
|
||||||
|
// adds dev section with ids, name, mf, mdl, via_device
|
||||||
|
// adds optional availability section
|
||||||
|
void Mqtt::add_ha_avail_section(JsonObject doc, const char * state_t, const bool is_first, const char * cond1, const char * cond2, const char * negcond) {
|
||||||
|
// only works for HA
|
||||||
|
if (discovery_type() != discoveryType::HOMEASSISTANT) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip availability section if no conditions set
|
// skip availability section if no conditions set
|
||||||
@@ -1414,7 +1413,7 @@ void Mqtt::add_ha_sections_to_doc(const char * name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// adds "availability" section to HA Discovery config
|
// adds "availability" section to HA Discovery config
|
||||||
JsonArray avty = config["avty"].to<JsonArray>();
|
JsonArray avty = doc["avty"].to<JsonArray>();
|
||||||
JsonDocument avty_json;
|
JsonDocument avty_json;
|
||||||
|
|
||||||
// make local copy of state, as the pointer will get de-referenced
|
// make local copy of state, as the pointer will get de-referenced
|
||||||
@@ -1451,22 +1450,23 @@ void Mqtt::add_ha_sections_to_doc(const char * name,
|
|||||||
avty.add(avty_json); // returns 0 if no mem
|
avty.add(avty_json); // returns 0 if no mem
|
||||||
}
|
}
|
||||||
|
|
||||||
config["avty_mode"] = "all";
|
doc["avty_mode"] = "all";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mqtt::add_ha_bool(JsonDocument & config) {
|
// adds the pl_on and pl_off sections to the doc
|
||||||
|
void Mqtt::add_ha_bool(JsonObject doc) {
|
||||||
const char * pl_on = "pl_on";
|
const char * pl_on = "pl_on";
|
||||||
const char * pl_off = "pl_off";
|
const char * pl_off = "pl_off";
|
||||||
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
|
||||||
config[pl_on] = true;
|
doc[pl_on] = true;
|
||||||
config[pl_off] = false;
|
doc[pl_off] = false;
|
||||||
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
|
||||||
config[pl_on] = 1;
|
doc[pl_on] = 1;
|
||||||
config[pl_off] = 0;
|
doc[pl_off] = 0;
|
||||||
} else {
|
} else {
|
||||||
char result[12];
|
char result[12];
|
||||||
config[pl_on] = Helpers::render_boolean(result, true);
|
doc[pl_on] = Helpers::render_boolean(result, true);
|
||||||
config[pl_off] = Helpers::render_boolean(result, false);
|
doc[pl_off] = Helpers::render_boolean(result, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class Mqtt {
|
|||||||
static bool queue_ha(const char * topic, const JsonObjectConst payload);
|
static bool queue_ha(const char * topic, const JsonObjectConst payload);
|
||||||
static bool queue_remove_topic(const char * topic);
|
static bool queue_remove_topic(const char * topic);
|
||||||
|
|
||||||
static bool publish_ha_sensor_config(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
|
static bool publish_ha_sensor_config_dv(DeviceValue & dv, const char * model, const char * brand, const bool remove, const bool create_device_config = false);
|
||||||
static bool publish_ha_sensor_config(uint8_t type,
|
static bool publish_ha_sensor_config(uint8_t type,
|
||||||
int8_t tag,
|
int8_t tag,
|
||||||
const char * const fullname,
|
const char * const fullname,
|
||||||
@@ -100,7 +100,9 @@ class Mqtt {
|
|||||||
const int16_t dv_set_min,
|
const int16_t dv_set_min,
|
||||||
const uint32_t dv_set_max,
|
const uint32_t dv_set_max,
|
||||||
const int8_t num_op,
|
const int8_t num_op,
|
||||||
const JsonObjectConst dev_json);
|
const char * const model = nullptr,
|
||||||
|
const char * const brand = nullptr,
|
||||||
|
const bool create_device_config = false);
|
||||||
|
|
||||||
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
|
static bool publish_system_ha_sensor_config(uint8_t type, const char * name, const char * entity, const uint8_t uom);
|
||||||
static bool publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
|
static bool publish_ha_climate_config(const int8_t tag, const bool has_roomtemp, const bool remove = false, const int16_t min = 5, const uint32_t max = 30);
|
||||||
@@ -243,16 +245,16 @@ class Mqtt {
|
|||||||
|
|
||||||
static std::string tag_to_topic(uint8_t device_type, int8_t tag);
|
static std::string tag_to_topic(uint8_t device_type, int8_t tag);
|
||||||
|
|
||||||
static void add_ha_uom(JsonObject doc, const uint8_t type, const uint8_t uom, const char * entity = nullptr, bool is_discovery = true);
|
static void
|
||||||
|
add_ha_classes(JsonObject doc, const uint8_t device_type, const uint8_t type, const uint8_t uom, const char * entity = nullptr, bool is_discovery = true);
|
||||||
static void add_ha_sections_to_doc(const char * name,
|
static void add_ha_dev_section(JsonObject doc, const char * name, const char * model, const char * brand, const bool create_model);
|
||||||
const char * state_t,
|
static void add_ha_avail_section(JsonObject doc,
|
||||||
JsonDocument & config,
|
const char * state_t,
|
||||||
const bool is_first = false,
|
const bool is_first,
|
||||||
const char * cond1 = nullptr,
|
const char * cond1 = nullptr,
|
||||||
const char * cond2 = nullptr,
|
const char * cond2 = nullptr,
|
||||||
const char * negcond = nullptr);
|
const char * negcond = nullptr);
|
||||||
static void add_ha_bool(JsonDocument & config);
|
static void add_ha_bool(JsonObject doc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static uuid::log::Logger logger_;
|
static uuid::log::Logger logger_;
|
||||||
|
|||||||
@@ -211,9 +211,9 @@ void Shower::create_ha_discovery() {
|
|||||||
snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str());
|
snprintf(stat_t, sizeof(stat_t), "%s/shower_active", Mqtt::base().c_str());
|
||||||
doc["stat_t"] = stat_t;
|
doc["stat_t"] = stat_t;
|
||||||
|
|
||||||
Mqtt::add_ha_bool(doc);
|
Mqtt::add_ha_bool(doc.as<JsonObject>());
|
||||||
|
Mqtt::add_ha_dev_section(doc.as<JsonObject>(), "Shower Sensor", nullptr, nullptr, false);
|
||||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, true); // create first dev & ids, no conditions
|
Mqtt::add_ha_avail_section(doc.as<JsonObject>(), stat_t, true); // no conditions
|
||||||
|
|
||||||
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
|
snprintf(topic, sizeof(topic), "binary_sensor/%s/shower_active/config", Mqtt::basename().c_str());
|
||||||
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
ha_configdone_ = Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
@@ -243,7 +243,8 @@ void Shower::create_ha_discovery() {
|
|||||||
doc["dev_cla"] = "duration";
|
doc["dev_cla"] = "duration";
|
||||||
// doc["ent_cat"] = "diagnostic";
|
// doc["ent_cat"] = "diagnostic";
|
||||||
|
|
||||||
Mqtt::add_ha_sections_to_doc("shower", stat_t, doc, false, "value_json.duration is defined");
|
Mqtt::add_ha_dev_section(doc.as<JsonObject>(), "Shower Sensor", nullptr, nullptr, false);
|
||||||
|
Mqtt::add_ha_avail_section(doc.as<JsonObject>(), stat_t, false, "value_json.duration is defined");
|
||||||
|
|
||||||
snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str());
|
snprintf(topic, sizeof(topic), "sensor/%s/shower_duration/config", Mqtt::basename().c_str());
|
||||||
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
Mqtt::queue_ha(topic, doc.as<JsonObject>()); // publish the config payload with retain flag
|
||||||
|
|||||||
@@ -520,7 +520,8 @@ void TemperatureSensor::publish_values(const bool force) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mqtt::add_ha_sections_to_doc("temperature", stat_t, config, !is_ha_device_created, val_cond);
|
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Temperature Sensors", nullptr, nullptr, false);
|
||||||
|
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !is_ha_device_created, val_cond);
|
||||||
|
|
||||||
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
char topic[Mqtt::MQTT_TOPIC_MAX_SIZE];
|
||||||
snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(temperaturesensor), sensor.id().c_str());
|
snprintf(topic, sizeof(topic), "sensor/%s/%s_%s/config", Mqtt::basename().c_str(), F_(temperaturesensor), sensor.id().c_str());
|
||||||
|
|||||||
@@ -326,15 +326,14 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
|
|||||||
|
|
||||||
// build the json for specific entity
|
// build the json for specific entity
|
||||||
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem & entity) {
|
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem & entity) {
|
||||||
output["name"] = entity.name;
|
output["name"] = entity.name;
|
||||||
output["fullname"] = entity.name;
|
output["fullname"] = entity.name;
|
||||||
output["storage"] = entity.ram ? "ram" : "ems";
|
output["storage"] = entity.ram ? "ram" : "ems";
|
||||||
output["type"] = entity.value_type == DeviceValueType::BOOL ? "boolean" : entity.value_type == DeviceValueType::STRING ? "string" : F_(number);
|
output["type"] = entity.value_type == DeviceValueType::BOOL ? "boolean" : entity.value_type == DeviceValueType::STRING ? "string" : F_(number);
|
||||||
// add uom state class and device class
|
|
||||||
Mqtt::add_ha_uom(output, entity.value_type, entity.uom, nullptr, false);
|
|
||||||
output["readable"] = true;
|
output["readable"] = true;
|
||||||
output["writeable"] = entity.writeable;
|
output["writeable"] = entity.writeable;
|
||||||
output["visible"] = true;
|
output["visible"] = true;
|
||||||
|
|
||||||
if (entity.ram == 0) {
|
if (entity.ram == 0) {
|
||||||
output["device_id"] = Helpers::hextoa(entity.device_id);
|
output["device_id"] = Helpers::hextoa(entity.device_id);
|
||||||
output["type_id"] = Helpers::hextoa(entity.type_id);
|
output["type_id"] = Helpers::hextoa(entity.type_id);
|
||||||
@@ -345,6 +344,10 @@ void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem
|
|||||||
output["bytes"] = (uint8_t)entity.factor;
|
output["bytes"] = (uint8_t)entity.factor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add uom state class and device class
|
||||||
|
Mqtt::add_ha_classes(output, EMSdevice::DeviceType::SYSTEM, entity.value_type, entity.uom, nullptr, false);
|
||||||
|
|
||||||
render_value(output, entity, true); // create the "value" field
|
render_value(output, entity, true); // create the "value" field
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,12 +448,12 @@ void WebCustomEntityService::publish(const bool force) {
|
|||||||
|
|
||||||
if (entityItem.value_type == DeviceValueType::BOOL) {
|
if (entityItem.value_type == DeviceValueType::BOOL) {
|
||||||
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
// applies to both Binary Sensor (read only) and a Switch (for a command)
|
||||||
Mqtt::add_ha_bool(config);
|
Mqtt::add_ha_bool(config.as<JsonObject>());
|
||||||
}
|
}
|
||||||
|
|
||||||
Mqtt::add_ha_uom(config.as<JsonObject>(), entityItem.value_type, entityItem.uom); // add uom
|
Mqtt::add_ha_classes(config.as<JsonObject>(), EMSdevice::DeviceType::SYSTEM, entityItem.value_type, entityItem.uom);
|
||||||
|
Mqtt::add_ha_dev_section(config.as<JsonObject>(), "Custom Entities", nullptr, nullptr, false);
|
||||||
Mqtt::add_ha_sections_to_doc(F_(custom), stat_t, config, !ha_created, val_cond);
|
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !ha_created, val_cond);
|
||||||
|
|
||||||
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
|
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,8 +287,9 @@ void WebSchedulerService::publish(const bool force) {
|
|||||||
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(scheduler), scheduleItem.name.c_str());
|
snprintf(command_topic, sizeof(command_topic), "%s/%s/%s", Mqtt::base().c_str(), F_(scheduler), scheduleItem.name.c_str());
|
||||||
config["cmd_t"] = command_topic;
|
config["cmd_t"] = command_topic;
|
||||||
|
|
||||||
Mqtt::add_ha_bool(config);
|
Mqtt::add_ha_bool(config.as<JsonObject>());
|
||||||
Mqtt::add_ha_sections_to_doc(F_(scheduler), stat_t, config, !ha_created, val_cond);
|
Mqtt::add_ha_dev_section(config.as<JsonObject>(), F_(scheduler), nullptr, nullptr, false);
|
||||||
|
Mqtt::add_ha_avail_section(config.as<JsonObject>(), stat_t, !ha_created, val_cond);
|
||||||
|
|
||||||
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
|
ha_created |= Mqtt::queue_ha(topic, config.as<JsonObject>());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user