From 8bd59e66d0003da7cf164bb25d8adb71afd5ec67 Mon Sep 17 00:00:00 2001 From: Indu Prakash Date: Wed, 5 Oct 2022 05:21:57 -0500 Subject: [PATCH] Added support for RGB --- src/httpserver/hass.c | 127 +++++++++++++++++++++++++++++++------- src/httpserver/hass.h | 12 +++- src/httpserver/http_fns.c | 41 +++++++----- 3 files changed, 140 insertions(+), 40 deletions(-) diff --git a/src/httpserver/hass.c b/src/httpserver/hass.c index c246217f2..22c53ea69 100644 --- a/src/httpserver/hass.c +++ b/src/httpserver/hass.c @@ -20,8 +20,12 @@ "stat_t":"ESPURNA-9DE8F9/relay/0", "cmd_t":"ESPURNA-9DE8F9/relay/0/set" } + +Abbreviated node names - https://www.home-assistant.io/docs/mqtt/discovery/ + */ + /// @brief Populates HomeAssistant unique id for the entity. /// @param type Entity type /// @param index Entity index @@ -29,15 +33,21 @@ void hass_populate_unique_id(ENTITY_TYPE type, int index, char *uniq_id){ //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(); + + switch(type){ + case ENTITY_LIGHT_PWM: + sprintf(uniq_id,"%s_%s_%d", longDeviceName, "light", index); + break; - //Entity type is `relay` or `light` - 5 char - if (type == ENTITY_LIGHT){ - sprintf(uniq_id,"%s_%s_%d", longDeviceName, "light", index); - } - else{ - sprintf(uniq_id,"%s_%s_%d", longDeviceName, "relay", index); + case ENTITY_LIGHT_RGB: + case ENTITY_LIGHT_RGBCW: + sprintf(uniq_id,"%s_%s", longDeviceName, "light"); + break; + + case ENTITY_RELAY: + sprintf(uniq_id,"%s_%s_%d", longDeviceName, "relay", index); + break; } } @@ -58,11 +68,16 @@ void hass_print_unique_id(http_request_t *request, const char *fmt, ENTITY_TYPE /// @param info Device info void hass_populate_device_config_channel(ENTITY_TYPE type, char *uniq_id, HassDeviceInfo *info){ //device_type is `switch` or `light` - if (type == ENTITY_LIGHT){ - sprintf(info->channel, "light/%s/config", uniq_id); - } - else{ - sprintf(info->channel, "switch/%s/config", uniq_id); + switch(type){ + case ENTITY_LIGHT_PWM: + case ENTITY_LIGHT_RGB: + case ENTITY_LIGHT_RGBCW: + sprintf(info->channel, "light/%s/config", uniq_id); + break; + + case ENTITY_RELAY: + sprintf(info->channel, "switch/%s/config", uniq_id); + break; } } @@ -88,21 +103,23 @@ cJSON *hass_build_device_node(cJSON *ids) { /// @param unique_id /// @param payload_on /// @param payload_off -void hass_populate_common(cJSON *root, int index, char *unique_id, char *payload_on, char *payload_off){ +/// @param isRGB If true, then state_topic and command_topic are not emitted. +void hass_populate_common(cJSON *root, int index, char *unique_id, char *payload_on, char *payload_off, bool isRGB){ const char *clientId = CFG_GetMQTTClientId(); //We are stuffing CFG_GetShortDeviceName and clientId into tmp so it needs to be bigger than them char tmp[MAX(CGF_MQTT_CLIENT_ID_SIZE, CGF_SHORT_DEVICE_NAME_SIZE) + 16]; - - //Using abbreviated node names as per https://www.home-assistant.io/docs/mqtt/discovery/ + sprintf(tmp,"%s %i",CFG_GetShortDeviceName(),index); cJSON_AddStringToObject(root, "name", tmp); - sprintf(tmp,"%s/%i/get",clientId,index); - cJSON_AddStringToObject(root, "stat_t", tmp); //state_topic + if (isRGB == false){ + sprintf(tmp,"%s/%i/get",clientId,index); + cJSON_AddStringToObject(root, "stat_t", tmp); //state_topic - sprintf(tmp,"%s/%i/set",clientId,index); - cJSON_AddStringToObject(root, "cmd_t", tmp); //command_topic + sprintf(tmp,"%s/%i/set",clientId,index); + cJSON_AddStringToObject(root, "cmd_t", tmp); //command_topic + } sprintf(tmp,"%s/connected",clientId); cJSON_AddStringToObject(root, "avty_t", tmp); //availability_topic @@ -117,8 +134,9 @@ void hass_populate_common(cJSON *root, int index, char *unique_id, char *payload /// @param index /// @param payload_on /// @param payload_off +/// @param isRGB If true, then state_topic and command_topic are not emitted. /// @return -HassDeviceInfo *hass_init_device_info(ENTITY_TYPE type, int index, char *payload_on, char *payload_off){ +HassDeviceInfo *hass_init_device_info(ENTITY_TYPE type, int index, char *payload_on, char *payload_off, bool isRGB){ HassDeviceInfo *info = os_malloc(sizeof(HassDeviceInfo)); addLogAdv(LOG_INFO, LOG_FEATURE_HASS, "hass_init_device_info=%p", info); @@ -135,11 +153,78 @@ HassDeviceInfo *hass_init_device_info(ENTITY_TYPE type, int index, char *payload cJSON_AddItemToObject(info->root, "dev", info->device); //device - hass_populate_common(info->root, index, info->unique_id, payload_on, payload_off); + hass_populate_common(info->root, index, info->unique_id, payload_on, payload_off, isRGB); addLogAdv(LOG_DEBUG, LOG_FEATURE_HASS, "root=%p", info->root); return info; } +/// @brief Initializes HomeAssistant relay device discovery storage. +/// @param index +/// @return +HassDeviceInfo *hass_init_relay_device_info(int index){ + return hass_init_device_info(ENTITY_RELAY, index, "1", "0", false); +} + +/// @brief Initializes HomeAssistant light device discovery storage. +/// @param type +/// @param index +/// @return +HassDeviceInfo *hass_init_light_device_info(ENTITY_TYPE type, int index){ + char tmp[CGF_MQTT_CLIENT_ID_SIZE + 64]; //Used to generate values based on CFG_GetMQTTClientId + const char *clientId = CFG_GetMQTTClientId(); + HassDeviceInfo *info = NULL; + + switch(type){ + case ENTITY_LIGHT_RGBCW: + case ENTITY_LIGHT_RGB: + info = hass_init_device_info(type, index, "1", "0", true); + + cJSON_AddStringToObject(info->root, "rgb_cmd_tpl","{{'#%02x%02x%02x0000'|format(red, green, blue)}}"); + + sprintf(tmp,"cmnd/%s/led_basecolor_rgb",clientId); + cJSON_AddStringToObject(info->root, "rgb_stat_t", tmp); + + sprintf(tmp,"cmnd/%s/led_basecolor_rgb",clientId); + cJSON_AddStringToObject(info->root, "rgb_cmd_t", tmp); + + sprintf(tmp,"cmnd/%s/led_enableAll",clientId); + cJSON_AddStringToObject(info->root, "cmd_t", tmp); + + sprintf(tmp,"cmnd/%s/led_dimmer",clientId); + cJSON_AddStringToObject(info->root, "bri_cmd_t", tmp); + cJSON_AddNumberToObject(info->root, "bri_scl", 100); + cJSON_AddStringToObject(info->root, "bri_val_tpl", "{{value_json.Dimmer}}"); + + if (type == ENTITY_LIGHT_RGBCW){ + sprintf(tmp,"cmnd/%s/led_temperature",clientId); + cJSON_AddStringToObject(info->root, "clr_temp_cmd_t", tmp); + + sprintf(tmp,"cmnd/%s/ctr",clientId); + cJSON_AddStringToObject(info->root, "clr_temp_stat_t", tmp); + + cJSON_AddStringToObject(info->root, "clr_temp_val_tpl", "{{value_json.CT}}"); + } + + break; + + case ENTITY_LIGHT_PWM: + info = hass_init_device_info(type, index, "99", "0", false); + cJSON_AddStringToObject(info->root, "on_cmd_type", "brightness"); //on_command_type + cJSON_AddNumberToObject(info->root, "bri_scl", 99); //brightness_scale + cJSON_AddNumberToObject(info->root, "bri_scl", 99); //brightness_scale + cJSON_AddBoolToObject(info->root, "opt", cJSON_True); //optimistic + + sprintf(tmp,"%s/%i/set",clientId,index); + cJSON_AddStringToObject(info->root, "bri_cmd_t", tmp); //brightness_command_topic + break; + + default: + addLogAdv(LOG_ERROR, LOG_FEATURE_HASS, "Unsupported light type %s", type); + } + + return info; +} + /// @brief Returns the discovery JSON. /// @param info /// @return diff --git a/src/httpserver/hass.h b/src/httpserver/hass.h index d893358fe..b5a94fc70 100644 --- a/src/httpserver/hass.h +++ b/src/httpserver/hass.h @@ -6,10 +6,15 @@ typedef enum { ENTITY_RELAY = 0, - ENTITY_LIGHT = 1 + ENTITY_LIGHT_PWM = 1, + ENTITY_LIGHT_RGB = 2, + ENTITY_LIGHT_RGBCW = 3, + ENTITY_SENSOR = 4, } ENTITY_TYPE; -//unique_id is based on CFG_GetDeviceName() whose size is CGF_DEVICE_NAME_SIZE (see hass_populate_unique_id) +//unique_id is defined in hass_populate_unique_id and is based on CFG_GetDeviceName() whose size is CGF_DEVICE_NAME_SIZE. +//Sample unique_id would be deviceName_entityType_index. +//Currently supported entityType is `relay` or `light` - 5 char. #define HASS_UNIQUE_ID_SIZE (CGF_DEVICE_NAME_SIZE + 1 + 5 + 1 + 4) //channel is based on unique_id (see hass_populate_device_config_channel) @@ -30,6 +35,7 @@ typedef struct HassDeviceInfo_s{ } HassDeviceInfo; void hass_print_unique_id(http_request_t *request, const char *fmt, ENTITY_TYPE type, int index); -HassDeviceInfo *hass_init_device_info(ENTITY_TYPE type, int index, char *payload_on, char *payload_off); +HassDeviceInfo *hass_init_relay_device_info(int index); +HassDeviceInfo *hass_init_light_device_info(ENTITY_TYPE type, int index); char *hass_build_discovery_json(HassDeviceInfo *info); void hass_free_device_info(HassDeviceInfo *info); diff --git a/src/httpserver/http_fns.c b/src/httpserver/http_fns.c index 692fd6081..fad8697ae 100644 --- a/src/httpserver/http_fns.c +++ b/src/httpserver/http_fns.c @@ -1226,10 +1226,17 @@ int http_fn_ha_discovery(http_request_t *request) { char topic[32]; int relayCount = 0; int pwmCount = 0; + char bLedDriverChipRunning; http_setup(request, httpMimeTypeText); get_Relay_PWM_Count(&relayCount, &pwmCount); +#ifndef OBK_DISABLE_ALL_DRIVERS + bLedDriverChipRunning = DRV_IsRunning("SM2135") || DRV_IsRunning("BP5758D"); +#else + bLedDriverChipRunning = 0; +#endif + if ((relayCount == 0) && (pwmCount == 0)) { poststr(request, NULL); return 0; @@ -1247,27 +1254,29 @@ int http_fn_ha_discovery(http_request_t *request) { if(relayCount > 0) { for(i = 0; i < CHANNEL_MAX; i++) { if(h_isChannelRelay(i)) { - HassDeviceInfo *dev_info = hass_init_device_info(ENTITY_RELAY, i, "1", "0"); + HassDeviceInfo *dev_info = hass_init_relay_device_info(i); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); } } } - if(pwmCount > 0) { - char tmp[64]; - const char *shortDeviceName = CFG_GetShortDeviceName(); - + if (pwmCount == 5 || bLedDriverChipRunning) { + // Enable + RGB control + CW control + HassDeviceInfo *dev_info = hass_init_light_device_info(ENTITY_LIGHT_RGBCW, -1); + MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); + hass_free_device_info(dev_info); + } + else if (pwmCount == 3) { + // Enable + RGB control + HassDeviceInfo *dev_info = hass_init_light_device_info(ENTITY_LIGHT_RGB, -1); + MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); + hass_free_device_info(dev_info); + } + else if(pwmCount > 0) { for(i = 0; i < CHANNEL_MAX; i++) { if(h_isChannelPWM(i)) { - HassDeviceInfo *dev_info = hass_init_device_info(ENTITY_LIGHT, i, "99", "0"); - - cJSON_AddStringToObject(dev_info->root, "on_cmd_type", "brightness"); //on_command_type - cJSON_AddNumberToObject(dev_info->root, "bri_scl", 99); //brightness_scale - - sprintf(tmp,"%s/%i/set",shortDeviceName,i); - cJSON_AddStringToObject(dev_info->root, "bri_cmd_t", tmp); //brightness_command_topic - + HassDeviceInfo *dev_info = hass_init_light_device_info(ENTITY_LIGHT_PWM, i); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); } @@ -1348,7 +1357,7 @@ int http_fn_ha_cfg(http_request_t *request) { switchAdded=1; } - hass_print_unique_id(request," - unique_id: \"%s\"\n", ENTITY_LIGHT,i); + hass_print_unique_id(request," - unique_id: \"%s\"\n", ENTITY_LIGHT_RGBCW,i); hprintf128(request," name: \"%s %i\"\n",shortDeviceName,i); hprintf128(request," rgb_command_template: \"{{ '#%%02x%%02x%%02x0000' | format(red, green, blue)}}\"\n"); hprintf128(request," rgb_value_template: \"{{ value[1:3] | int(base=16) }},{{ value[3:5] | int(base=16) }},{{ value[5:7] | int(base=16) }}\"\n"); @@ -1377,7 +1386,7 @@ int http_fn_ha_cfg(http_request_t *request) { switchAdded=1; } - hass_print_unique_id(request," - unique_id: \"%s\"\n", ENTITY_LIGHT,i); + hass_print_unique_id(request," - unique_id: \"%s\"\n", ENTITY_LIGHT_RGB,i); hprintf128(request," name: \"%s %i\"\n",shortDeviceName,i); hprintf128(request," rgb_command_template: \"{{ '#%%02x%%02x%%02x0000' | format(red, green, blue)}}\"\n"); hprintf128(request," rgb_value_template: \"{{ value[1:3] | int(base=16) }},{{ value[3:5] | int(base=16) }},{{ value[5:7] | int(base=16) }}\"\n"); @@ -1405,7 +1414,7 @@ int http_fn_ha_cfg(http_request_t *request) { lightAdded=1; } - hass_print_unique_id(request," - unique_id: \"%s\"\n", ENTITY_LIGHT,i); + hass_print_unique_id(request," - unique_id: \"%s\"\n", ENTITY_LIGHT_PWM,i); hprintf128(request," name: \"%s %i\"\n",shortDeviceName,i); hprintf128(request," state_topic: \"%s/%i/get\"\n",clientId,i); hprintf128(request," command_topic: \"%s/%i/set\"\n",clientId,i);