Twin BL0942 mod - Two BL0942 on two UARTs on one power metering device (#1531)

* Twin BL0942 mod

* Energy data more checks for index 0

* BL09XX_ResetEnergyCounter reset lastSavedEnergyCounterValue

* added ENABLE_BL_TWIN compiler option, default disabled

* added more ENABLE_BL_TWIN
This commit is contained in:
XJ
2025-02-26 11:51:22 +01:00
committed by GitHub
parent 974d97acda
commit 3c22b5e4ad
12 changed files with 1010 additions and 467 deletions

View File

@ -32,7 +32,8 @@ const char *g_template_lowMidHigh = "{% if value == '0' %}\n"
/// @param type Entity type
/// @param index Entity index (Ignored for RGB)
/// @param uniq_id Array to populate (should be of size HASS_UNIQUE_ID_SIZE)
void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id) {
/// @param asensdatasetix dataset index for ENERGY_METER_SENSOR, otherwise 0
void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id, int asensdatasetix) {
//https://developers.home-assistant.io/docs/entity_registry_index/#unique-id-requirements
//mentions that mac can be used for unique_id and deviceName contains that.
const char* longDeviceName = CFG_GetDeviceName();
@ -54,7 +55,7 @@ void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id) {
case ENERGY_METER_SENSOR:
#ifdef ENABLE_DRIVER_BL0937
sprintf(uniq_id, "%s_sensor_%s", longDeviceName, DRV_GetEnergySensorNames(index)->hass_uniq_id_suffix);
sprintf(uniq_id, "%s_sensor_%s", longDeviceName, DRV_GetEnergySensorNamesEx(asensdatasetix,index)->hass_uniq_id_suffix);
#endif
break;
case POWER_SENSOR:
@ -133,9 +134,10 @@ void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id) {
/// @param fmt
/// @param type Entity type
/// @param index Entity index
void hass_print_unique_id(http_request_t* request, const char* fmt, ENTITY_TYPE type, int index) {
/// @param asensdatasetix dataset index for ENERGY_METER_SENSOR, otherwise 0
void hass_print_unique_id(http_request_t* request, const char* fmt, ENTITY_TYPE type, int index, int asensdatasetix) {
char uniq_id[HASS_UNIQUE_ID_SIZE];
hass_populate_unique_id(type, index, uniq_id);
hass_populate_unique_id(type, index, uniq_id, asensdatasetix);
hprintf255(request, fmt, uniq_id);
}
@ -207,12 +209,13 @@ cJSON* hass_build_device_node(cJSON* ids) {
/// For energy sensors, index corresponds to energySensor_t. For regular sensor, index can be be the channel.
/// @param payload_on The payload that represents enabled state. This is not added for POWER_SENSOR.
/// @param payload_off The payload that represents disabled state. This is not added for POWER_SENSOR.
/// @param asensdatasetix dataset index for ENERGY_METER_SENSOR, otherwise 0
/// @return
HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* payload_on, const char* payload_off) {
HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* payload_on, const char* payload_off, int asensdatasetix) {
HassDeviceInfo* info = os_malloc(sizeof(HassDeviceInfo));
addLogAdv(LOG_DEBUG, LOG_FEATURE_HASS, "hass_init_device_info=%p", info);
hass_populate_unique_id(type, index, info->unique_id);
hass_populate_unique_id(type, index, info->unique_id, asensdatasetix);
hass_populate_device_config_channel(type, info->unique_id, info);
info->ids = cJSON_CreateArray();
@ -247,7 +250,7 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* p
isSensor = true;
#ifdef ENABLE_DRIVER_BL0937
if (index <= OBK__LAST)
sprintf(g_hassBuffer, "%s", DRV_GetEnergySensorNames(index)->name_friendly);
sprintf(g_hassBuffer, "%s", DRV_GetEnergySensorNamesEx(asensdatasetix, index)->name_friendly);
else
sprintf(g_hassBuffer, "Unknown Energy Meter Sensor");
#endif
@ -355,10 +358,10 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* p
HassDeviceInfo* hass_init_relay_device_info(int index, ENTITY_TYPE type, bool bToggleInv) {
HassDeviceInfo* info;
if (bToggleInv) {
info = hass_init_device_info(type, index, "0", "1");
info = hass_init_device_info(type, index, "0", "1", 0);
}
else {
info = hass_init_device_info(type, index, "1", "0");
info = hass_init_device_info(type, index, "1", "0", 0);
}
sprintf(g_hassBuffer, "~/%i/get", index);
@ -380,7 +383,7 @@ HassDeviceInfo* hass_init_light_device_info(ENTITY_TYPE type) {
//We can just use 1 to generate unique_id and name for single PWM.
//The payload_on/payload_off have to match the state_topic/command_topic values.
info = hass_init_device_info(type, 1, "1", "0");
info = hass_init_device_info(type, 1, "1", "0", 0);
switch (type) {
case LIGHT_RGBCW:
@ -451,7 +454,7 @@ HassDeviceInfo* hass_init_binary_sensor_device_info(int index, bool bInverse) {
payload_off = "1";
payload_on = "0";
}
HassDeviceInfo* info = hass_init_device_info(BINARY_SENSOR, index, payload_on, payload_off);
HassDeviceInfo* info = hass_init_device_info(BINARY_SENSOR, index, payload_on, payload_off, 0);
sprintf(g_hassBuffer, "~/%i/get", index);
cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer); //state_topic
@ -464,7 +467,7 @@ HassDeviceInfo* hass_init_binary_sensor_device_info(int index, bool bInverse) {
/// @brief Initializes HomeAssistant power sensor device discovery storage.
/// @param index Index corresponding to energySensor_t.
/// @return
HassDeviceInfo* hass_init_energy_sensor_device_info(int index) {
HassDeviceInfo* hass_init_energy_sensor_device_info(int index, int asensdatasetix) {
HassDeviceInfo* info = 0;
//https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes
@ -472,15 +475,15 @@ HassDeviceInfo* hass_init_energy_sensor_device_info(int index) {
if (index > OBK__LAST) return info;
if (index >= OBK_CONSUMPTION__DAILY_FIRST && !DRV_IsRunning("NTP")) return info; //include daily stats only when time is valid
info = hass_init_device_info(ENERGY_METER_SENSOR, index, NULL, NULL);
info = hass_init_device_info(ENERGY_METER_SENSOR, index, NULL, NULL, asensdatasetix);
cJSON_AddStringToObject(info->root, "dev_cla", DRV_GetEnergySensorNames(index)->hass_dev_class); //device_class=voltage,current,power, energy, timestamp
cJSON_AddStringToObject(info->root, "dev_cla", DRV_GetEnergySensorNamesEx(asensdatasetix,index)->hass_dev_class); //device_class=voltage,current,power, energy, timestamp
//20241024 XJIKKA unit_of_meas is set bellow (was set twice)
//cJSON_AddStringToObject(info->root, "unit_of_meas", DRV_GetEnergySensorNames(index)->units); //unit_of_measurement. Sets as empty string if not present. HA doesn't seem to mind
sprintf(g_hassBuffer, "~/%s/get", DRV_GetEnergySensorNames(index)->name_mqtt);
sprintf(g_hassBuffer, "~/%s/get", DRV_GetEnergySensorNamesEx(asensdatasetix, index)->name_mqtt);
cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer);
if (!strcmp(DRV_GetEnergySensorNames(index)->hass_dev_class, "energy")) {
if (!strcmp(DRV_GetEnergySensorNamesEx(asensdatasetix, index)->hass_dev_class, "energy")) {
//state_class can be measurement, total or total_increasing. Energy values should be total_increasing.
cJSON_AddStringToObject(info->root, "stat_cla", "total_increasing");
cJSON_AddStringToObject(info->root, "unit_of_meas", CFG_HasFlag(OBK_FLAG_MQTT_ENERGY_IN_KWH) ? "kWh" : "Wh");
@ -488,14 +491,14 @@ HassDeviceInfo* hass_init_energy_sensor_device_info(int index) {
//20241024 XJIKKA skip measurement for timestamp - HASS log:
//HASS: energy_clear_date (<class 'homeassistant.components.mqtt.sensor.MqttSensor'>) is using state class 'measurement'
// which is impossible considering device class ('timestamp') it is using; expected None;
if (strcmp(DRV_GetEnergySensorNames(index)->hass_dev_class,"timestamp")) {
if (strcmp(DRV_GetEnergySensorNamesEx(asensdatasetix, index)->hass_dev_class,"timestamp")) {
cJSON_AddStringToObject(info->root, "stat_cla", "measurement");
}
//20241024 XJIKKA if unit is not set (drv_bl_shared.c @ "power_factor"), mqtt value unit_of_meas was empty - HASS log:
//HASS: sensor...power_factor is using native unit of measurement '' which is not a valid unit
// for the device class ('power_factor') it is using; expected one of ['no unit of measurement', '%'];
//solution is to skip empty
if (strlen(DRV_GetEnergySensorNames(index)->units)>0) {
if (strlen(DRV_GetEnergySensorNamesEx(asensdatasetix, index)->units)>0) {
cJSON_AddStringToObject(info->root, "unit_of_meas", DRV_GetEnergySensorNames(index)->units);
}
}
@ -537,7 +540,7 @@ HassDeviceInfo* hass_init_light_singleColor_onChannels(int toggle, int dimmer, i
const char* clientId;
clientId = CFG_GetMQTTClientId();
dev_info = hass_init_device_info(LIGHT_PWM, toggle, "1", "0");
dev_info = hass_init_device_info(LIGHT_PWM, toggle, "1", "0", 0);
sprintf(g_hassBuffer, "~/%i/get", toggle);
cJSON_AddStringToObject(dev_info->root, "stat_t", g_hassBuffer); //state_topic
@ -559,7 +562,7 @@ HassDeviceInfo* hass_init_light_singleColor_onChannels(int toggle, int dimmer, i
/// @return
HassDeviceInfo* hass_init_sensor_device_info(ENTITY_TYPE type, int channel, int decPlaces, int decOffset, int divider) {
//Assuming that there is only one DHT setup per device which keeps uniqueid/names simpler
HassDeviceInfo* info = hass_init_device_info(type, channel, NULL, NULL); //using channel as index to generate uniqueId
HassDeviceInfo* info = hass_init_device_info(type, channel, NULL, NULL, 0); //using channel as index to generate uniqueId
//https://developers.home-assistant.io/docs/core/entity/sensor/#available-device-classes
switch (type) {

View File

@ -120,11 +120,11 @@ typedef struct HassDeviceInfo_s {
cJSON* ids;
} HassDeviceInfo;
void hass_print_unique_id(http_request_t* request, const char* fmt, ENTITY_TYPE type, int index);
void hass_print_unique_id(http_request_t* request, const char* fmt, ENTITY_TYPE type, int index, int asensdatasetix);
HassDeviceInfo* hass_init_relay_device_info(int index, ENTITY_TYPE type, bool bInverse);
HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* payload_on, const char* payload_off);
HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* payload_on, const char* payload_off, int asensdatasetix);
HassDeviceInfo* hass_init_light_device_info(ENTITY_TYPE type);
HassDeviceInfo* hass_init_energy_sensor_device_info(int index);
HassDeviceInfo* hass_init_energy_sensor_device_info(int index, int asensdatasetix);
HassDeviceInfo* hass_init_light_singleColor_onChannels(int toggle, int dimmer, int brightness_scale);
HassDeviceInfo* hass_init_binary_sensor_device_info(int index, bool bInverse);
HassDeviceInfo* hass_init_sensor_device_info(ENTITY_TYPE type, int channel, int decPlaces, int decOffset, int divider);

View File

@ -7,6 +7,7 @@
#include "../cmnds/cmd_public.h"
#include "../driver/drv_tuyaMCU.h"
#include "../driver/drv_public.h"
#include "../driver/drv_bl_shared.h"
#include "../hal/hal_wifi.h"
#include "../hal/hal_pins.h"
#include "../hal/hal_flashConfig.h"
@ -1823,13 +1824,30 @@ void doHomeAssistantDiscovery(const char* topic, http_request_t* request) {
if (measuringPower == true) {
for (i = OBK__FIRST; i <= OBK__LAST; i++)
{
dev_info = hass_init_energy_sensor_device_info(i);
dev_info = hass_init_energy_sensor_device_info(i, BL_SENSORS_IX_0);
if (dev_info) {
MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN);
hass_free_device_info(dev_info);
discoveryQueued = true;
}
}
#if ENABLE_BL_TWIN
//BL_SENSORS_IX_1 - mqtt hass discovery using hass_uniq_id_suffix (_b) from drv_bl_shared.c
if (BL_IsMeteringDeviceIndexActive(BL_SENSORS_IX_1)) {
for (i = OBK__FIRST; i <= OBK__LAST; i++)
{
//BL_SENSORS_IX_1 does not have energy yet, just base OBK_VOLTAGE..OBK_POWER_FACTOR
if (i < OBK_VOLTAGE) continue;
if (i > OBK_POWER_FACTOR) continue;
dev_info = hass_init_energy_sensor_device_info(i, BL_SENSORS_IX_1);
if (dev_info) {
MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN);
hass_free_device_info(dev_info);
discoveryQueued = true;
}
}
}
#endif
}
#endif
@ -2264,7 +2282,7 @@ int http_fn_ha_cfg(http_request_t* request) {
switchAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", RELAY, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", RELAY, i, 0);
hprintf255(request, " name: %i\n", i);
hprintf255(request, " state_topic: \"%s/%i/get\"\n", clientId, i);
hprintf255(request, " command_topic: \"%s/%i/set\"\n", clientId, i);
@ -2284,7 +2302,7 @@ int http_fn_ha_cfg(http_request_t* request) {
switchAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", BINARY_SENSOR, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", BINARY_SENSOR, i, 0);
hprintf255(request, " name: %i\n", i);
hprintf255(request, " state_topic: \"%s/%i/get\"\n", clientId, i);
hprintf_qos_payload(request, clientId);
@ -2303,7 +2321,7 @@ int http_fn_ha_cfg(http_request_t* request) {
switchAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_RGBCW, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_RGBCW, i, 0);
hprintf255(request, " name: %i\n", i);
http_generate_rgb_cfg(request, clientId);
//hprintf255(request, " #brightness_value_template: \"{{ value }}\"\n");
@ -2323,7 +2341,7 @@ int http_fn_ha_cfg(http_request_t* request) {
switchAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_RGB, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_RGB, i, 0);
hprintf255(request, " name: Light\n");
http_generate_rgb_cfg(request, clientId);
}
@ -2338,7 +2356,7 @@ int http_fn_ha_cfg(http_request_t* request) {
switchAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_PWM, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_PWM, i, 0);
hprintf255(request, " name: Light\n");
http_generate_singleColor_cfg(request, clientId);
}
@ -2353,7 +2371,7 @@ int http_fn_ha_cfg(http_request_t* request) {
switchAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_PWMCW, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_PWMCW, i, 0);
hprintf255(request, " name: Light\n");
http_generate_cw_cfg(request, clientId);
}
@ -2370,7 +2388,7 @@ int http_fn_ha_cfg(http_request_t* request) {
lightAdded = 1;
}
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_PWM, i);
hass_print_unique_id(request, " - unique_id: \"%s\"\n", LIGHT_PWM, i, 0);
hprintf255(request, " name: %i\n", i);
hprintf255(request, " state_topic: \"%s/%i/get\"\n", clientId, i);
hprintf255(request, " command_topic: \"%s/%i/set\"\n", clientId, i);