#include "../new_common.h" #include "http_fns.h" #include "../new_pins.h" #include "../new_cfg.h" #include "../hal/hal_ota.h" // Commands register, execution API and cmd tokenizer #include "../cmnds/cmd_public.h" #include "../cmnds/cmd_enums.h" #include "../driver/drv_tuyaMCU.h" #include "../driver/drv_girierMCU.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" #include "../logging/logging.h" #include "../devicegroups/deviceGroups_public.h" #include "../mqtt/new_mqtt.h" #include "hass.h" #include "../cJSON/cJSON.h" #include #include "../driver/drv_ntp.h" #include "../driver/drv_local.h" #ifdef PLATFORM_BEKEN #include "start_type_pub.h" #endif #ifdef WINDOWS // nothing #elif PLATFORM_BL602 #include #include // For BL602 ADC HAL #include // For BL602 ADC Standard Driver #include // For BL602 Global Register Standard Driver #include //For BL602 WiFi AP Scan #elif PLATFORM_W600 || PLATFORM_W800 #elif PLATFORM_XRADIO #include #include #elif defined(PLATFORM_BK7231N) // tuya-iotos-embeded-sdk-wifi-ble-bk7231n/sdk/include/tuya_hal_storage.h #include "tuya_hal_storage.h" #include "BkDriverFlash.h" #include "temp_detect_pub.h" #elif defined(PLATFORM_LN882H) #elif defined(PLATFORM_TR6260) #elif defined(PLATFORM_REALTEK) && !PLATFORM_REALTEK_NEW #include "wifi_structures.h" #include "wifi_constants.h" #include "wifi_conf.h" extern uint32_t current_fw_idx; #ifdef PLATFORM_RTL87X0C #include "hal_sys_ctrl.h" extern hal_reset_reason_t reset_reason; #endif SemaphoreHandle_t scan_hdl; #elif PLATFORM_REALTEK_NEW #include "lwip_netconf.h" #include "ameba_soc.h" #include "ameba_ota.h" extern uint32_t current_fw_idx; #elif defined(PLATFORM_ESPIDF) || PLATFORM_ESP8266 #include "esp_wifi.h" #include "esp_system.h" #elif defined(PLATFORM_BK7231T) // REALLY? A typo in Tuya SDK? Storge? // tuya-iotos-embeded-sdk-wifi-ble-bk7231t/platforms/bk7231t/tuya_os_adapter/include/driver/tuya_hal_storge.h #include "tuya_hal_storge.h" #include "BkDriverFlash.h" #include "temp_detect_pub.h" #elif defined(PLATFORM_ECR6600) #include "hal_system.h" #endif #if (defined(PLATFORM_BK7231T) || defined(PLATFORM_BK7231N)) && !defined(PLATFORM_BEKEN_NEW) int tuya_os_adapt_wifi_all_ap_scan(AP_IF_S** ap_ary, unsigned int* num); int tuya_os_adapt_wifi_release_ap(AP_IF_S* ap); #endif static const char SUBMIT_AND_END_FORM[] = "
"; const char* g_typesOffLowMidHigh[] = { "Off","Low","Mid","High" }; const char* g_typesOffLowMidHighHighest[] = { "Off", "Low","Mid","High","Highest" }; const char* g_typesOffLowestLowMidHighHighest[] = { "Off", "Lowest", "Low", "Mid", "High", "Highest" }; const char* g_typesLowMidHighHighest[] = { "Low","Mid","High","Highest" }; const char* g_typesOffOnRemember[] = { "Off", "On", "Remember" }; const char* g_typeLowMidHigh[] = { "Low","Mid","High" }; const char* g_typesLowestLowMidHighHighest[] = { "Lowest", "Low", "Mid", "High", "Highest" };; const char* g_typeOpenStopClose[] = { "Open","Stop","Close" }; const char* g_typeStopUpDown[] = { "Stop","Up","Down" }; #define ADD_OPTION(t,a) if(type == t) { *numTypes = sizeof(a)/sizeof(a[0]); return a; } const char **Channel_GetOptionsForChannelType(int type, int *numTypes) { ADD_OPTION(ChType_OffLowMidHigh, g_typesOffLowMidHigh); ADD_OPTION(ChType_OffLowestLowMidHighHighest, g_typesOffLowestLowMidHighHighest); ADD_OPTION(ChType_LowestLowMidHighHighest, g_typesLowestLowMidHighHighest); ADD_OPTION(ChType_OffLowMidHighHighest, g_typesOffLowMidHighHighest); ADD_OPTION(ChType_LowMidHighHighest, g_typesLowMidHighHighest); ADD_OPTION(ChType_OffOnRemember, g_typesOffOnRemember); ADD_OPTION(ChType_LowMidHigh, g_typeLowMidHigh); ADD_OPTION(ChType_OpenStopClose, g_typeOpenStopClose); ADD_OPTION(ChType_StopUpDown, g_typeStopUpDown); *numTypes = 0; return 0; } unsigned char hexdigit(char hex) { return (hex <= '9') ? hex - '0' : toupper((unsigned char)hex) - 'A' + 10; } unsigned char hexbyte(const char* hex) { return (hexdigit(*hex) << 4) | hexdigit(*(hex + 1)); } int http_fn_empty_url(http_request_t* request) { poststr(request, "HTTP/1.1 302 OK\nLocation: /index\nConnection: close\n\n"); poststr(request, NULL); return 0; } void postFormAction(http_request_t* request, char* action, char* value) { //"
" hprintf255(request, "
", action, value); } void poststr_h2(http_request_t* request, const char* content) { hprintf255(request, "

%s

", content); } void poststr_h4(http_request_t* request, const char* content) { hprintf255(request, "

%s

", content); } /// @brief Generate a pair of label and field elements for Name type entry. The field is limited to entry of a-zA-Z0-9_- characters. /// @param request /// @param label /// @param fieldId This also gets used as the field name /// @param value /// @param preContent void add_label_name_field(http_request_t* request, char* label, char* fieldId, const char* value, char* preContent) { if (strlen(preContent) > 0) { poststr(request, preContent); } hprintf255(request, "
", fieldId, label); hprintf255(request, ""); } /// @brief Generate a pair of label and field elements. /// @param request /// @param label /// @param fieldId This also gets used as the field name /// @param value /// @param preContent void add_label_input(http_request_t* request, char* inputType, char* label, char* fieldId, const char* value, char* preContent) { if (strlen(preContent) > 0) { poststr(request, preContent); } hprintf255(request, "
", fieldId, label); hprintf255(request, ""); } /// @brief Generates a pair of label and text field elements. /// @param request /// @param label Label for the field /// @param fieldId Field id, this also gets used as the name /// @param value String value /// @param preContent Content before the label void add_label_text_field(http_request_t* request, char* label, char* fieldId, const char* value, char* preContent) { add_label_input(request, "text", label, fieldId, value, preContent); } /// @brief Generates a pair of label and text field elements. /// @param request /// @param label Label for the field /// @param fieldId Field id, this also gets used as the name /// @param value String value /// @param preContent Content before the label void add_label_password_field(http_request_t* request, char* label, char* fieldId, const char* value, char* preContent) { add_label_input(request, "password", label, fieldId, value, preContent); } /// @brief Generate a pair of label and numeric field elements. /// @param request /// @param label Label for the field /// @param fieldId Field id, this also gets used as the name /// @param value Integer value /// @param preContent Content before the label void add_label_numeric_field(http_request_t* request, char* label, char* fieldId, int value, char* preContent) { char strValue[32]; sprintf(strValue, "%i", value); add_label_input(request, "number", label, fieldId, strValue, preContent); } int http_fn_testmsg(http_request_t* request) { poststr(request, "This is just a test msg\n\n"); poststr(request, NULL); return 0; } // bit mask telling which channels are hidden from HTTP // If given bit is set, then given channel is hidden extern int g_hiddenChannels; int http_fn_index(http_request_t* request) { int j, i, ch1, ch2; char tmpA[128]; int bRawPWMs; bool bForceShowRGBCW; float fValue; int iValue; bool bForceShowRGB; const char* inputName; int channelType; bRawPWMs = CFG_HasFlag(OBK_FLAG_LED_RAWCHANNELSMODE); bForceShowRGBCW = CFG_HasFlag(OBK_FLAG_LED_FORCESHOWRGBCWCONTROLLER); bForceShowRGB = CFG_HasFlag(OBK_FLAG_LED_FORCE_MODE_RGB); // user override is always stronger, so if no override set if (bForceShowRGB == false && bForceShowRGBCW == false) { #ifndef OBK_DISABLE_ALL_DRIVERS if (DRV_IsRunning("SM16703P")) { bForceShowRGB = true; } if (DRV_IsRunning("DMX")) { bForceShowRGB = true; } #endif } http_setup(request, httpMimeTypeHTML); //Add mimetype regardless of the request // use ?state URL parameter to only request current state if (!http_getArg(request->url, "state", tmpA, sizeof(tmpA))) { // full update - include header http_html_start(request, NULL); poststr(request, "
"); #if defined(PLATFORM_BEKEN) || defined(WINDOWS) if (DRV_IsRunning("PWMToggler")) { DRV_Toggler_ProcessChanges(request); } #endif #if defined(PLATFORM_BEKEN) || defined(WINDOWS) if (DRV_IsRunning("httpButtons")) { DRV_HTTPButtons_ProcessChanges(request); } #endif if (http_getArg(request->url, "tgl", tmpA, sizeof(tmpA))) { j = atoi(tmpA); if (j == SPECIAL_CHANNEL_LEDPOWER) { hprintf255(request, "

Toggled LED power!

", j); } else { hprintf255(request, "

Toggled %s!

", CHANNEL_GetLabel(j)); } CHANNEL_Toggle(j); } if (http_getArg(request->url, "on", tmpA, sizeof(tmpA))) { j = atoi(tmpA); hprintf255(request, "

Enabled %s!

", CHANNEL_GetLabel(j)); CHANNEL_Set(j, 255, 1); } #if ENABLE_LED_BASIC if (http_getArg(request->url, "rgb", tmpA, sizeof(tmpA))) { hprintf255(request, "

Set RGB to %s!

", tmpA); LED_SetBaseColor(0, "led_basecolor", tmpA, 0); // auto enable - but only for changes made from WWW panel // This happens when users changes COLOR if (CFG_HasFlag(OBK_FLAG_LED_AUTOENABLE_ON_WWW_ACTION)) { LED_SetEnableAll(true); } } #endif if (http_getArg(request->url, "off", tmpA, sizeof(tmpA))) { j = atoi(tmpA); hprintf255(request, "

Disabled %s!

", CHANNEL_GetLabel(j)); CHANNEL_Set(j, 0, 1); } if (http_getArg(request->url, "pwm", tmpA, sizeof(tmpA))) { int newPWMValue = atoi(tmpA); http_getArg(request->url, "pwmIndex", tmpA, sizeof(tmpA)); j = atoi(tmpA); if (j == SPECIAL_CHANNEL_TEMPERATURE) { hprintf255(request, "

Changed Temperature to %i!

", newPWMValue); } else { hprintf255(request, "

Changed pwm %i to %i!

", j, newPWMValue); } CHANNEL_Set(j, newPWMValue, 1); #if ENABLE_LED_BASIC if (j == SPECIAL_CHANNEL_TEMPERATURE) { // auto enable - but only for changes made from WWW panel // This happens when users changes TEMPERATURE if (CFG_HasFlag(OBK_FLAG_LED_AUTOENABLE_ON_WWW_ACTION)) { LED_SetEnableAll(true); } } #endif } if (http_getArg(request->url, "dim", tmpA, sizeof(tmpA))) { int newDimmerValue = atoi(tmpA); http_getArg(request->url, "dimIndex", tmpA, sizeof(tmpA)); j = atoi(tmpA); if (j == SPECIAL_CHANNEL_BRIGHTNESS) { hprintf255(request, "

Changed LED brightness to %i!

", newDimmerValue); } else { hprintf255(request, "

Changed dimmer %i to %i!

", j, newDimmerValue); } CHANNEL_Set(j, newDimmerValue, 1); #if ENABLE_LED_BASIC if (j == SPECIAL_CHANNEL_BRIGHTNESS) { // auto enable - but only for changes made from WWW panel // This happens when users changes DIMMER if (CFG_HasFlag(OBK_FLAG_LED_AUTOENABLE_ON_WWW_ACTION)) { LED_SetEnableAll(true); } } #endif } if (http_getArg(request->url, "set", tmpA, sizeof(tmpA))) { int newSetValue = atoi(tmpA); http_getArg(request->url, "setIndex", tmpA, sizeof(tmpA)); j = atoi(tmpA); hprintf255(request, "

Changed channel %s to %i!

", CHANNEL_GetLabel(j), newSetValue); CHANNEL_Set(j, newSetValue, 1); } if (http_getArg(request->url, "restart", tmpA, sizeof(tmpA))) { poststr(request, "
Module will restart soon
"); RESET_ScheduleModuleReset(3); } if (http_getArg(request->url, "unsafe", tmpA, sizeof(tmpA))) { poststr(request, "
Will try to do unsafe init in few seconds
"); MAIN_ScheduleUnsafeInit(3); } poststr(request, "
"); // end div#change #if ENABLE_OBK_BERRY void Berry_SaveRequest(http_request_t *r); Berry_SaveRequest(request); CMD_Berry_RunEventHandlers_StrPtr(CMD_EVENT_ON_HTTP, "prestate", request); #endif #ifndef OBK_DISABLE_ALL_DRIVERS DRV_AppendInformationToHTTPIndexPage(request, true); #endif poststr(request, "
"); // replaceable content follows } #if ENABLE_OBK_BERRY void Berry_SaveRequest(http_request_t *r); Berry_SaveRequest(request); CMD_Berry_RunEventHandlers_StrPtr(CMD_EVENT_ON_HTTP, "state", request); #endif if (!CFG_HasFlag(OBK_FLAG_HTTP_NO_ONOFF_WORDS)){ poststr(request, ""); //Table default to 100% width in stylesheet for (i = 0; i < CHANNEL_MAX; i++) { channelType = CHANNEL_GetType(i); // check ability to hide given channel from gui if (BIT_CHECK(g_hiddenChannels, i)) { continue; // hidden } bool bToggleInv = channelType == ChType_Toggle_Inv; if (h_isChannelRelay(i) || channelType == ChType_Toggle) { if (i <= 1) { hprintf255(request, ""); } if (CHANNEL_Check(i) != bToggleInv) { poststr(request, ""); } else { poststr(request, ""); } if (i == CHANNEL_MAX - 1) { poststr(request, ""); } } } poststr(request, "
ONOFF
"); } poststr(request, ""); //Table default to 100% width in stylesheet for (i = 0; i < CHANNEL_MAX; i++) { // check ability to hide given channel from gui if (BIT_CHECK(g_hiddenChannels, i)) { continue; // hidden } channelType = CHANNEL_GetType(i); bool bToggleInv = channelType == ChType_Toggle_Inv; if (h_isChannelRelay(i) || channelType == ChType_Toggle || bToggleInv) { const char* c; const char* prefix; if (i <= 1) { hprintf255(request, ""); } if (CHANNEL_Check(i) != bToggleInv) { c = "bgrn"; } else { c = "bred"; } poststr(request, "", c, prefix, CHANNEL_GetLabel(i)); if (i == CHANNEL_MAX - 1) { poststr(request, ""); } } } poststr(request, "
"); hprintf255(request, "", i); if (CHANNEL_ShouldAddTogglePrefixToUI(i)) { prefix = "Toggle "; } else { prefix = ""; } hprintf255(request, "
"); poststr(request, ""); //Table default to 100% width in stylesheet for (i = 0; i < PLATFORM_GPIO_MAX; i++) { int role; role = PIN_GetPinRoleForPinIndex(i); if (IS_PIN_DHT_ROLE(role)) { // DHT pin has two channels - temperature and humidity poststr(request, ""); } } for (i = 0; i < CHANNEL_MAX; i++) { const char **types; int numTypes; // check ability to hide given channel from gui if (BIT_CHECK(g_hiddenChannels, i)) { continue; // hidden } channelType = CHANNEL_GetType(i); if (channelType == ChType_TimerSeconds) { iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_ReadOnlyLowMidHigh) { const char* types[] = { "Low","Mid","High" }; iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_Enum) { iValue = CHANNEL_Get(i); channelEnum_t *en; // if setChannelEnum has not been defined, treat ChType_Enum as a textfield if (g_enums == NULL || g_enums[i]->numOptions == 0 ) { //en = g_enums[i]; poststr(request, ""); } else { en = g_enums[i]; poststr(request, ""); } } else if (channelType == ChType_ReadOnlyEnum) { iValue = CHANNEL_Get(i); const char* oLabel; if (g_enums == NULL || g_enums[i]->numOptions == 0) oLabel = CHANNEL_GetLabel(i); else oLabel = CMD_FindChannelEnumLabel(g_enums[i], iValue); poststr(request, ""); } else if ((types = Channel_GetOptionsForChannelType(channelType, &numTypes)) != 0) { const char *what; if (channelType == ChType_OffOnRemember) { what = "memory"; } else if (channelType == ChType_OpenStopClose || channelType == ChType_StopUpDown) { what = "mode"; } else { what = "speed"; } iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_TextField) { iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_ReadOnly) { iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_Motion || channelType == ChType_Motion_n) { iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_OpenClosed) { iValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_OpenClosed_Inv) { iValue = CHANNEL_Get(i); poststr(request, ""); } else if (h_isChannelRelay(i) || channelType == ChType_Toggle || channelType == ChType_Toggle_Inv) { // HANDLED ABOVE in previous loop } else if ((bRawPWMs && h_isChannelPWM(i)) || (channelType == ChType_Dimmer) || (channelType == ChType_Dimmer256) || (channelType == ChType_Dimmer1000) || channelType == ChType_Percent) { int maxValue; // PWM and dimmer both use a slider control inputName = h_isChannelPWM(i) ? "pwm" : "dim"; int pwmValue; if (channelType == ChType_Dimmer256) { maxValue = 255; } else if (channelType == ChType_Dimmer1000) { maxValue = 1000; } else { maxValue = 100; } pwmValue = CHANNEL_Get(i); poststr(request, ""); } else if (channelType == ChType_OffDimBright) { const char* types[] = { "Off","Dim","Bright" }; iValue = CHANNEL_Get(i); poststr(request, ""); } else { const char *channelTitle; channelTitle = ChannelType_GetTitle(channelType); if (*channelTitle) { int div; const char *channelUnit; char formatStr[16]; strcpy(formatStr, " %.4f"); div = ChannelType_GetDivider(channelType); channelUnit = ChannelType_GetUnit(channelType); iValue = CHANNEL_Get(i); fValue = (float)iValue / (float)div; poststr(request, ""); } } } bool bForceShowSingleDimmer = 0; #if ENABLE_DRIVER_GOSUNDSW2 if (DRV_IsRunning("GosundSW2")) { bForceShowSingleDimmer = 1; } #endif #if ENABLE_LED_BASIC if (bRawPWMs == 0 || bForceShowRGBCW || bForceShowRGB || bForceShowSingleDimmer || LED_IsLedDriverChipRunning()) { int c_pwms; int lm; int c_realPwms = 0; lm = LED_GetMode(); //c_pwms = PIN_CountPinsWithRoleOrRole(IOR_PWM, IOR_PWM_n); // This will treat multiple PWMs on a single channel as one. // Thanks to this users can turn for example RGB LED controller // into high power 3-outputs single colors LED controller PIN_get_Relay_PWM_Count(0, &c_pwms, 0); c_realPwms = c_pwms; if (LED_IsLedDriverChipRunning()) { c_pwms = CFG_CountLEDRemapChannels(); } if (bForceShowSingleDimmer) { c_pwms = 1; } else if (bForceShowRGBCW) { c_pwms = 5; } else if (bForceShowRGB) { c_pwms = 3; } if (c_pwms > 0) { const char* c; if (CHANNEL_Check(SPECIAL_CHANNEL_LEDPOWER)) { c = "bgrn"; } else { c = "bred"; } poststr(request, ""); } if (c_pwms > 0) { int pwmValue; inputName = "dim"; pwmValue = LED_GetDimmer(); poststr(request, ""); } if (c_pwms >= 3) { char colorValue[16]; inputName = "rgb"; const char* activeStr = ""; if (lm == Light_RGB) { activeStr = "[ACTIVE]"; } LED_GetBaseColorString(colorValue); poststr(request, ""); } bool bShowCWForPixelAnim = false; #if ENABLE_DRIVER_PIXELANIM if (DRV_IsRunning("PixelAnim")) { if (c_realPwms == 2) bShowCWForPixelAnim = true; PixelAnim_CreatePanel(request); } #endif if (c_pwms == 2 || c_pwms >= 4 || bShowCWForPixelAnim) { // TODO: temperature slider int pwmValue; const char* activeStr = ""; if (lm == Light_Temperature) { activeStr = "[ACTIVE]"; } inputName = "pwm"; pwmValue = LED_GetTemperature(); long pwmKelvin = HASS_TO_KELVIN(pwmValue); long pwmKelvinMax = HASS_TO_KELVIN(led_temperature_min); long pwmKelvinMin = HASS_TO_KELVIN(led_temperature_max); poststr(request, ""); } } #endif #if defined(PLATFORM_BEKEN) || defined(WINDOWS) if (DRV_IsRunning("PWMToggler")) { DRV_Toggler_AddToHtmlPage(request); } #endif #if defined(PLATFORM_BEKEN) || defined(WINDOWS) if (DRV_IsRunning("httpButtons")) { DRV_HTTPButtons_AddToHtmlPage(request); } #endif poststr(request, "
"); ch1 = PIN_GetPinChannelForPinIndex(i); ch2 = PIN_GetPinChannel2ForPinIndex(i); iValue = CHANNEL_Get(ch1); hprintf255(request, "Sensor %s on pin %i temperature %.2fC", PIN_RoleToString(role), i, (float)(iValue * 0.1f)); iValue = CHANNEL_Get(ch2); hprintf255(request, ", humidity %.1f%%
", (float)iValue); if (ch1 == ch2) { hprintf255(request, "WARNING: you have the same channel set twice for DHT, please fix in pins config, set two different channels"); } poststr(request, "
"); hprintf255(request, "Timer Channel %s value ", CHANNEL_GetLabel(i)); if (iValue < 60) { hprintf255(request, "%i seconds
", iValue); } else if (iValue < 3600) { int minutes = iValue / 60; int seconds = iValue % 60; hprintf255(request, "%i minutes %i seconds
", minutes, seconds); } else { int hours = iValue / 3600; int remainingSeconds = iValue % 3600; int minutes = remainingSeconds / 60; int seconds = remainingSeconds % 60; hprintf255(request, "%i hours %i minutes %i seconds
", hours, minutes, seconds); } poststr(request, "
"); if (iValue >= 0 && iValue <= 2) { hprintf255(request, "Channel %s = %s", CHANNEL_GetLabel(i), types[iValue]); } else { hprintf255(request, "Channel %s = %i", CHANNEL_GetLabel(i), iValue); } poststr(request, "
"); hprintf255(request, "

Change channel %s enum:

", CHANNEL_GetLabel(i)); hprintf255(request, "", i); hprintf255(request, "", iValue); hprintf255(request, "
"); hprintf255(request, ""); poststr(request, "
"); hprintf255(request, "
", i, CHANNEL_GetLabel(i)); hprintf255(request, "", i); hprintf255(request, "
"); poststr(request, "
"); hprintf255(request, "Channel %s = %s [%i]", CHANNEL_GetLabel(i), oLabel, iValue); poststr(request, "
"); hprintf255(request, "

Select %s:

", what); hprintf255(request, "", i); for (j = 0; j < numTypes; j++) { const char* check; if (j == iValue) check = "checked"; else check = ""; hprintf255(request, "%s", j, check, types[j]); } hprintf255(request, "
"); poststr(request, "
"); hprintf255(request, "

Change channel %s value:

", CHANNEL_GetLabel(i)); hprintf255(request, "", i); hprintf255(request, "", iValue); hprintf255(request, "
"); hprintf255(request, ""); poststr(request, "
"); hprintf255(request, "Channel %s = %i", CHANNEL_GetLabel(i), iValue); poststr(request, "
"); if (iValue == (channelType != ChType_Motion)) { hprintf255(request, "No motion (ch %i)", i); } else { hprintf255(request, "Motion! (ch %i)", i); } poststr(request, "
"); if (iValue) { hprintf255(request, "CLOSED (ch %i)", i); } else { hprintf255(request, "OPEN (ch %i)", i); } poststr(request, "
"); if (!iValue) { hprintf255(request, "CLOSED (ch %i)", i); } else { hprintf255(request, "OPEN (ch %i)", i); } poststr(request, "
"); hprintf255(request, "Channel %s:
", CHANNEL_GetLabel(i), i); hprintf255(request, "", maxValue, inputName, i, pwmValue); hprintf255(request, "", inputName, i); hprintf255(request, "
", CHANNEL_GetLabel(i)); poststr(request, "
"); hprintf255(request, "

Select level:

"); hprintf255(request, "", i); for (j = 0; j < 3; j++) { const char* check; if (j == iValue) check = "checked"; else check = ""; hprintf255(request, "%s", j, check, types[j]); } hprintf255(request, "
"); poststr(request, "
"); poststr(request, channelTitle); // how many decimal places? formatStr[3] = '0'+ChannelType_GetDecimalPlaces(channelType); hprintf255(request, formatStr, fValue); poststr(request, channelUnit); hprintf255(request, " (%s)", CHANNEL_GetLabel(i)); poststr(request, "
"); poststr(request, "
"); hprintf255(request, "", SPECIAL_CHANNEL_LEDPOWER); hprintf255(request, "
", c); poststr(request, "
"); hprintf255(request, "
LED Dimmer/Brightness
"); hprintf255(request, "
", SPECIAL_CHANNEL_BRIGHTNESS); hprintf255(request, "", inputName, SPECIAL_CHANNEL_BRIGHTNESS, pwmValue); hprintf255(request, "", inputName, SPECIAL_CHANNEL_BRIGHTNESS); hprintf255(request, "
", SPECIAL_CHANNEL_BRIGHTNESS); poststr(request, "
"); hprintf255(request, "
LED RGB Color %s
", activeStr); hprintf255(request, "
", SPECIAL_CHANNEL_BASECOLOR); // onchange would fire only if colour was changed // onblur will fire every time hprintf255(request, "", inputName, SPECIAL_CHANNEL_BASECOLOR, colorValue); hprintf255(request, "", inputName, SPECIAL_CHANNEL_BASECOLOR); hprintf255(request, "
"); poststr(request, "
"); hprintf255(request, "
LED Temperature Slider %s (%ld K) (Warm <--- ---> Cool)
", activeStr, pwmKelvin); hprintf255(request, "
", SPECIAL_CHANNEL_TEMPERATURE); //(KELVIN_TEMPERATURE_MAX - KELVIN_TEMPERATURE_MIN) / (HASS_TEMPERATURE_MAX - HASS_TEMPERATURE_MIN) = 13 hprintf255(request, "", pwmKelvin); hprintf255(request, "", inputName, SPECIAL_CHANNEL_TEMPERATURE); hprintf255(request, "", SPECIAL_CHANNEL_TEMPERATURE, inputName); poststr(request, "
"); #ifndef OBK_DISABLE_ALL_DRIVERS DRV_AppendInformationToHTTPIndexPage(request, false); #endif if (1) { int bFirst = true; hprintf255(request, "
"); for (i = 0; i < CHANNEL_MAX; i++) { if (CHANNEL_IsInUse(i)) { float value = CHANNEL_GetFloat(i); if (bFirst == false) { hprintf255(request, ", "); } hprintf255(request, "Channel %i = %.2f", i, value); bFirst = false; } } if (1) { i = RepeatingEvents_GetActiveCount(); if (i) { if (bFirst == false) { hprintf255(request, ", "); } hprintf255(request, "%i repeating events", i); bFirst = false; } i = EventHandlers_GetActiveCount(); if (i) { if (bFirst == false) { hprintf255(request, ", "); } hprintf255(request, "%i event handlers", i); bFirst = false; } #if defined(WINDOWS) || defined(PLATFORM_BEKEN) i = CMD_GetCountActiveScriptThreads(); if (i) { if (bFirst == false) { hprintf255(request, ", "); } hprintf255(request, "%i script threads", i); bFirst = false; } #endif } hprintf255(request, "
"); } hprintf255(request, "
Cfg size: %i, change counter: %i, ota counter: %i, incomplete boots: %i
", sizeof(g_cfg), g_cfg.changeCounter, g_cfg.otaCounter, g_bootFailures); // display temperature - thanks to giedriuslt // only in Normal mode, and if boot is not failing #ifndef NO_CHIP_TEMPERATURE hprintf255(request, "
Chip temperature: %.1f°C
", g_wifi_temperature); #endif #if ENABLE_PING_WATCHDOG inputName = CFG_GetPingHost(); if (inputName && *inputName && CFG_GetPingDisconnectedSecondsToRestart()) { hprintf255(request, "
Ping watchdog (%s) - ", inputName); if (g_startPingWatchDogAfter > 0) { hprintf255(request, "will start in %i!
", g_startPingWatchDogAfter); } else { hprintf255(request, "%i lost, %i ok, last reply was %is ago!", PingWatchDog_GetTotalLost(), PingWatchDog_GetTotalReceived(), g_timeSinceLastPingReply); } } #endif if (Main_HasWiFiConnected()) { int rssi = HAL_GetWifiStrength(); hprintf255(request, "
Wifi RSSI: %s (%idBm)
", str_rssi[wifi_rssi_scale(rssi)], rssi); } #if PLATFORM_BEKEN /* typedef enum { RESET_SOURCE_POWERON = 0, RESET_SOURCE_REBOOT = 1, RESET_SOURCE_WATCHDOG = 2, RESET_SOURCE_DEEPPS_GPIO = 3, RESET_SOURCE_DEEPPS_RTC = 4, RESET_SOURCE_CRASH_XAT0 = 5, RESET_SOURCE_CRASH_UNDEFINED = 6, RESET_SOURCE_CRASH_PREFETCH_ABORT = 7, RESET_SOURCE_CRASH_DATA_ABORT = 8, RESET_SOURCE_CRASH_UNUSED = 9, } RESET_SOURCE_STATUS; */ { const char* s = "Unk"; if (g_rebootReason == 0) s = "Pwr"; else if (g_rebootReason == 1) s = "Rbt"; else if (g_rebootReason == 2) s = "Wdt"; else if (g_rebootReason == 3) s = "Pin Interrupt"; else if (g_rebootReason == 4) s = "Sleep Timer"; hprintf255(request, "
Reboot reason: %i - %s
", g_rebootReason, s); } #elif PLATFORM_BL602 char reason[26]; bl_sys_rstinfo_getsting(reason); hprintf255(request, "
Reboot reason: %s
", reason); #elif PLATFORM_LN882H // type is chip_reboot_cause_t g_rebootReason = ln_chip_get_reboot_cause(); { const char* s = "Unk"; if (g_rebootReason == 0) s = "Pwr"; else if (g_rebootReason == 1) s = "Soft"; else if (g_rebootReason == 2) s = "Wdt"; hprintf255(request, "
Reboot reason: %i - %s
", g_rebootReason, s); } #elif PLATFORM_ESPIDF esp_reset_reason_t reason = esp_reset_reason(); const char* s = "Unknown"; switch(reason) { case ESP_RST_UNKNOWN: s = "ESP_RST_UNKNOWN"; break; case ESP_RST_POWERON: s = "ESP_RST_POWERON"; break; case ESP_RST_EXT: s = "ESP_RST_EXT"; break; case ESP_RST_SW: s = "ESP_RST_SW"; break; case ESP_RST_PANIC: s = "ESP_RST_PANIC"; break; case ESP_RST_INT_WDT: s = "ESP_RST_INT_WDT"; break; case ESP_RST_TASK_WDT: s = "ESP_RST_TASK_WDT"; break; case ESP_RST_WDT: s = "ESP_RST_WDT"; break; case ESP_RST_DEEPSLEEP: s = "ESP_RST_DEEPSLEEP"; break; case ESP_RST_BROWNOUT: s = "ESP_RST_BROWNOUT"; break; case ESP_RST_SDIO: s = "ESP_RST_SDIO"; break; case ESP_RST_USB: s = "ESP_RST_USB"; break; case ESP_RST_JTAG: s = "ESP_RST_JTAG"; break; case ESP_RST_EFUSE: s = "ESP_RST_EFUSE"; break; case ESP_RST_PWR_GLITCH: s = "ESP_RST_PWR_GLITCH"; break; case ESP_RST_CPU_LOCKUP: s = "ESP_RST_CPU_LOCKUP"; break; default: break; } hprintf255(request, "
Reboot reason: %i - %s
", reason, s); #elif PLATFORM_RTL87X0C const char* s = "Unk"; switch(reset_reason) { case HAL_RESET_REASON_POWER_ON: s = "Pwr"; break; case HAL_RESET_REASON_SOFTWARE: s = "Soft"; break; case HAL_RESET_REASON_WATCHDOG: s = "Wdt"; break; default: break; } hprintf255(request, "
Reboot reason: %i - %s
", reset_reason, s); hprintf255(request, "
Current fw: FW%i
", current_fw_idx); #elif PLATFORM_RTL8710B || PLATFORM_RTL8720D || PLATFORM_REALTEK_NEW hprintf255(request, "
Current fw: FW%i
", current_fw_idx + 1); #elif PLATFORM_ECR6600 RST_TYPE reset_type = hal_get_reset_type(); const char* s; switch(reset_type) { case RST_TYPE_POWER_ON: s = "POWER_ON"; break; case RST_TYPE_FATAL_EXCEPTION: s = "FATAL_EXCEPTION"; break; case RST_TYPE_SOFTWARE_REBOOT: s = "SOFTWARE_REBOOT"; break; case RST_TYPE_HARDWARE_REBOOT: s = "HARDWARE_REBOOT"; break; case RST_TYPE_OTA: s = "OTA"; break; case RST_TYPE_WAKEUP: s = "WAKEUP"; break; case RST_TYPE_HARDWARE_WDT_TIMEOUT: s = "HARDWARE_WDT_TIMEOUT"; break; case RST_TYPE_SOFTWARE_WDT_TIMEOUT: s = "SOFTWARE_WDT_TIMEOUT"; break; case RST_TYPE_UNKOWN: s = "UNKNOWN"; break; default: s = "ERROR"; break; } hprintf255(request, "
Reboot reason: %i - %s
", reset_type, s); #endif #if ENABLE_MQTT if (CFG_GetMQTTHost()[0] == 0) { hprintf255(request, "
MQTT State: not configured
"); } else { const char* stateStr; const char* colorStr; if (mqtt_reconnect > 0) { stateStr = "awaiting reconnect"; colorStr = "orange"; } else if (Main_HasMQTTConnected() == 1) { stateStr = "connected"; colorStr = "green"; } else { stateStr = "disconnected"; colorStr = "yellow"; } hprintf255(request, "
MQTT State: %s RES: %d(%s)
", colorStr, stateStr, MQTT_GetConnectResult(), get_error_name(MQTT_GetConnectResult())); hprintf255(request, "MQTT ErrMsg: %s
", (MQTT_GetStatusMessage() != NULL) ? MQTT_GetStatusMessage() : ""); hprintf255(request, "MQTT Stats: CONN: %d PUB: %d RECV: %d ERR: %d
", MQTT_GetConnectEvents(), MQTT_GetPublishEventCounter(), MQTT_GetReceivedEventCounter(), MQTT_GetPublishErrorCounter()); } #endif /* Format current PINS input state for all unused pins */ if (CFG_HasFlag(OBK_FLAG_HTTP_PINMONITOR)) { for (i = 0; i < PLATFORM_GPIO_MAX; i++) { if ((PIN_GetPinRoleForPinIndex(i) == IOR_None) && (i != 0) && (i != 1)) { HAL_PIN_Setup_Input(i); } } hprintf255(request, "
PIN States
"); for (i = 0; i < PLATFORM_GPIO_MAX; i++) { if ((PIN_GetPinRoleForPinIndex(i) != IOR_None) || (i == 0) || (i == 1)) { hprintf255(request, "P%02i: NA ", i); } else { hprintf255(request, "P%02i: %i ", i, (int)HAL_PIN_ReadDigitalInput(i)); } if (i % 10 == 9) { hprintf255(request, "
"); } } hprintf255(request, "
"); } #if ENABLE_DRIVER_CHARTS /* // moved from drv_charts.c: // on every "state" request, JS code will be loaded and canvas is redrawn // this leads to a flickering graph // so put this right below the "state" div // with a "#ifdef // drawback : We need to take care, if driver is loaded and canvas will be displayed only on a reload of the page // or we might try and hide/unhide it ... */ // since we can't simply stop showing the graph in updated status, we need to "hide" it if driver was stopped if (! DRV_IsRunning("Charts")) { poststr(request, ""); }; #endif if (OTA_GetProgress() >= 0) { hprintf255(request, "
OTA In Progress. Downloaded: %i B Flashed: %06lXh
", OTA_GetTotalBytes(), OTA_GetProgress()); } if (bSafeMode) { hprintf255(request, "
You are in safe mode (AP mode) because full reboot failed %i times. ", g_bootFailures); hprintf255(request, "Pins, relays, etc are disabled.
"); } // for normal page loads, show the rest of the HTML if (!http_getArg(request->url, "state", tmpA, sizeof(tmpA))) { poststr(request, "
"); // end div#state #if ENABLE_DRIVER_CHARTS /* // moved from drv_charts.c: // on every "state" request, JS code will be loaded and canvas is redrawn // this leads to a flickering graph // so put this right below the "state" div // with a "#ifdef // drawback : We need to take care, if driver is loaded and canvas will be displayed only on a reload of the page // or we might try and hide/unhide it ... */ // if (DRV_IsRunning("Charts")) { poststr(request, ""); poststr(request, ""); // }; #endif // Shared UI elements poststr(request, "
"); poststr(request, "
" "" "" "
"); if (bSafeMode) { poststr(request, "
" "" "" "
"); } poststr(request, "
"); poststr(request, "
"); poststr(request, htmlFooterRefreshLink); http_html_end(request); } poststr(request, NULL); return 0; } int http_fn_about(http_request_t* request) { http_setup(request, httpMimeTypeHTML); http_html_start(request, "About"); poststr_h2(request, "Open source firmware for BK7231N, BK7231T, T34, BL2028N, XR809, W600/W601, W800/W801, BL602, LF686 and LN882H by OpenSHWProjects"); poststr(request, htmlFooterReturnToMainPage); http_html_end(request); poststr(request, NULL); return 0; } #if ENABLE_HTTP_MQTT int http_fn_cfg_mqtt_set(http_request_t* request) { char tmpA[128]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Saving MQTT"); if (http_getArg(request->url, "host", tmpA, sizeof(tmpA))) { } // FIX: always set, so people can clear field CFG_SetMQTTHost(tmpA); if (http_getArg(request->url, "port", tmpA, sizeof(tmpA))) { CFG_SetMQTTPort(atoi(tmpA)); } #if MQTT_USE_TLS CFG_SetMQTTUseTls(http_getArg(request->url, "mqtt_use_tls", tmpA, sizeof(tmpA))); CFG_SetMQTTVerifyTlsCert(http_getArg(request->url, "mqtt_verify_tls_cert", tmpA, sizeof(tmpA))); http_getArg(request->url, "mqtt_cert_file", tmpA, sizeof(tmpA)); CFG_SetMQTTCertFile(tmpA); #endif if (http_getArg(request->url, "user", tmpA, sizeof(tmpA))) { CFG_SetMQTTUserName(tmpA); } if (http_getArg(request->url, "password", tmpA, sizeof(tmpA))) { CFG_SetMQTTPass(tmpA); } if (http_getArg(request->url, "client", tmpA, sizeof(tmpA))) { CFG_SetMQTTClientId(tmpA); } if (http_getArg(request->url, "group", tmpA, sizeof(tmpA))) { CFG_SetMQTTGroupTopic(tmpA); } CFG_Save_SetupTimer(); poststr(request, "Please wait for module to connect... if there is problem, restart it from Index html page..."); #if ENABLE_MQTT g_mqtt_bBaseTopicDirty = 1; #endif poststr(request, "
Return to MQTT settings
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } int http_fn_cfg_mqtt(http_request_t* request) { http_setup(request, httpMimeTypeHTML); http_html_start(request, "MQTT"); poststr_h2(request, "Use this to connect to your MQTT"); poststr_h4(request, "To disable MQTT, clear the host field."); hprintf255(request, "

Command topic: cmnd/%s/[Command]

", CFG_GetMQTTClientId()); hprintf255(request, "

Publish data topic: %s/[Channel]/get

", CFG_GetMQTTClientId()); hprintf255(request, "

Receive data topic: %s/[Channel]/set

", CFG_GetMQTTClientId()); add_label_text_field(request, "Host", "host", CFG_GetMQTTHost(), "
"); add_label_numeric_field(request, "Port", "port", CFG_GetMQTTPort(), "
"); #if MQTT_USE_TLS hprintf255(request, ""); } hprintf255(request, "
"); hprintf255(request, ""); } hprintf255(request, "
"); add_label_text_field(request, "Certificate File (CA Root or Public Certificate PEM format)", "mqtt_cert_file", CFG_GetMQTTCertFile(), "
"); #endif add_label_text_field(request, "Client Topic (Base Topic)", "client", CFG_GetMQTTClientId(), "

"); add_label_text_field(request, "Group Topic (Secondary Topic to only receive cmnds)", "group", CFG_GetMQTTGroupTopic(), "
"); add_label_text_field(request, "User", "user", CFG_GetMQTTUserName(), "
"); add_label_password_field(request, "Password", "password", CFG_GetMQTTPass(), "
"); poststr(request, "
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif #if ENABLE_HTTP_IP int http_fn_cfg_ip(http_request_t* request) { char tmp[64]; int g_changes = 0; http_setup(request, httpMimeTypeHTML); http_html_start(request, "IP"); poststr_h2(request, "Here you can set static IP or DHCP"); hprintf255(request, "

This setting applies only to WiFi client mode.

"); hprintf255(request, "

You must restart manually for changes to take place.

"); hprintf255(request, "

Currently, DHCP is enabled by default and works when you set IP to 0.0.0.0.

"); if (http_getArg(request->url, "IP", tmp, sizeof(tmp))) { str_to_ip(tmp, g_cfg.staticIP.localIPAddr); hprintf255(request, "
IP=%s (%02x %02x %02x %02x)
",tmp,g_cfg.staticIP.localIPAddr[0],g_cfg.staticIP.localIPAddr[1],g_cfg.staticIP.localIPAddr[2],g_cfg.staticIP.localIPAddr[3]); g_changes++; } if (http_getArg(request->url, "mask", tmp, sizeof(tmp))) { str_to_ip(tmp, g_cfg.staticIP.netMask); hprintf255(request, "
Mask=%s (%02x %02x %02x %02x)
",tmp, g_cfg.staticIP.netMask[0], g_cfg.staticIP.netMask[1], g_cfg.staticIP.netMask[2], g_cfg.staticIP.netMask[3]); g_changes++; } if (http_getArg(request->url, "dns", tmp, sizeof(tmp))) { str_to_ip(tmp, g_cfg.staticIP.dnsServerIpAddr); hprintf255(request, "
DNS=%s (%02x %02x %02x %02x)
",tmp, g_cfg.staticIP.dnsServerIpAddr[0], g_cfg.staticIP.dnsServerIpAddr[1], g_cfg.staticIP.dnsServerIpAddr[2], g_cfg.staticIP.dnsServerIpAddr[3]); g_changes++; } if (http_getArg(request->url, "gate", tmp, sizeof(tmp))) { str_to_ip(tmp, g_cfg.staticIP.gatewayIPAddr); hprintf255(request, "
GW=%s (%02x %02x %02x %02x)
",tmp, g_cfg.staticIP.gatewayIPAddr[0], g_cfg.staticIP.gatewayIPAddr[1], g_cfg.staticIP.gatewayIPAddr[2], g_cfg.staticIP.gatewayIPAddr[3]); g_changes++; } if (g_changes) { CFG_MarkAsDirty(); hprintf255(request, "

Saved.

"); } convert_IP_to_string(tmp, g_cfg.staticIP.localIPAddr); add_label_text_field(request, "IP", "IP", tmp, "
"); convert_IP_to_string(tmp, g_cfg.staticIP.netMask); add_label_text_field(request, "Mask", "mask", tmp, "

"); convert_IP_to_string(tmp, g_cfg.staticIP.dnsServerIpAddr); add_label_text_field(request, "DNS", "dns", tmp, "
"); convert_IP_to_string(tmp, g_cfg.staticIP.gatewayIPAddr); add_label_text_field(request, "Gate", "gate", tmp, "
"); poststr(request, "
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif #if ENABLE_HTTP_WEBAPP int http_fn_cfg_webapp(http_request_t* request) { http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set Webapp"); add_label_text_field(request, "URL of the Webapp", "url", CFG_GetWebappRoot(), "
"); #if MQTT_USE_TLS hprintf255(request, ""); } hprintf255(request, "
"); #endif poststr(request, SUBMIT_AND_END_FORM); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } int http_fn_cfg_webapp_set(http_request_t* request) { char tmpA[128]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Saving Webapp"); if (http_getArg(request->url, "url", tmpA, sizeof(tmpA))) { CFG_SetWebappRoot(tmpA); CFG_Save_IfThereArePendingChanges(); hprintf255(request, "Webapp url set to %s", tmpA); } else { poststr(request, "Webapp url not set because you didn't specify the argument."); } #if MQTT_USE_TLS CFG_SetDisableWebServer(!http_getArg(request->url, "enable_web_server", tmpA, sizeof(tmpA))); if (CFG_GetDisableWebServer()) { poststr(request, "
"); poststr(request, "Webapp will be disabled on next boot!"); } #endif poststr(request, "
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif #if ENABLE_HTTP_PING int http_fn_cfg_ping(http_request_t* request) { char tmpA[128]; int bChanged; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set Watchdog"); bChanged = 0; poststr(request, "

Ping watchdog (backup reconnect mechanism)

"); poststr(request, "

By default, all OpenBeken devices automatically try to reconnect to WiFi when a connection is lost."); poststr(request, " I have tested the reconnect mechanism many times by restarting my router and it always worked reliably."); poststr(request, " However, according to some reports, there are still some edge cases where a device fails to reconnect to WiFi."); poststr(request, " This is why this mechanism has been added.

"); poststr(request, "

This mechanism continuously pings a specified host and reconnects to WiFi if it doesn't respond for the specified number of seconds.

"); poststr(request, "

USAGE: For the host, choose the main address of your router and ensure it responds to pings. The interval is around 1 second, and the timeout can be set by the user, for example, to 60 seconds.

"); if (http_getArg(request->url, "host", tmpA, sizeof(tmpA))) { CFG_SetPingHost(tmpA); poststr_h4(request, "New ping host set!"); bChanged = 1; } /* if(http_getArg(request->url,"interval",tmpA,sizeof(tmpA))) { CFG_SetPingIntervalSeconds(atoi(tmpA)); poststr(request,"

New ping interval set!

"); bChanged = 1; }*/ if (http_getArg(request->url, "disconnectTime", tmpA, sizeof(tmpA))) { CFG_SetPingDisconnectedSecondsToRestart(atoi(tmpA)); poststr_h4(request, "New ping disconnectTime set!"); bChanged = 1; } if (http_getArg(request->url, "clear", tmpA, sizeof(tmpA))) { CFG_SetPingDisconnectedSecondsToRestart(0); CFG_SetPingIntervalSeconds(0); CFG_SetPingHost(""); poststr_h4(request, "Ping watchdog disabled!"); bChanged = 1; } if (bChanged) { CFG_Save_IfThereArePendingChanges(); poststr_h4(request, "Changes will be applied after restarting"); } poststr(request, "\ \ \
"); poststr_h2(request, "Use this to enable pinger"); add_label_text_field(request, "Host", "host", CFG_GetPingHost(), "
"); add_label_numeric_field(request, "Take action after this number of seconds with no reply", "disconnectTime", CFG_GetPingDisconnectedSecondsToRestart(), "
"); poststr(request, "

\ \
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif int http_fn_cfg_wifi(http_request_t* request) { // for a test, show password as well... char tmpA[128]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set Wifi"); /*bChanged = 0; if(http_getArg(recvbuf,"ssid",tmpA,sizeof(tmpA))) { CFG_SetWiFiSSID(tmpA); poststr(request,"

WiFi SSID set!

"); bChanged = 1; } if(http_getArg(recvbuf,"pass",tmpA,sizeof(tmpA))) { CFG_SetWiFiPass(tmpA); poststr(request,"

WiFi Password set!

"); bChanged = 1; } if(bChanged) { poststr(request,"

Device will reconnect after restarting

"); }*/ poststr(request, "

Check networks reachable by module

This will take a few seconds
"); if (http_getArg(request->url, "scan", tmpA, sizeof(tmpA))) { #ifdef WINDOWS poststr(request, "Not available on Windows
"); #elif PLATFORM_BL602 wifi_mgmr_ap_item_t *ap_info; uint32_t i, ap_num; bk_printf("Scan begin...\r\n"); wifi_mgmr_all_ap_scan(&ap_info, &ap_num); bk_printf("Scan returned %li networks\r\n", ap_num); for (i = 0; i < ap_num; i++) { hprintf255(request, "[%i/%i] SSID: %s, Channel: %i, Signal %i
", (i+1), (int)ap_num, ap_info[i].ssid, ap_info[i].channel, ap_info[i].rssi); } vPortFree(ap_info); #elif defined(PLATFORM_BK7231T) && !defined(PLATFORM_BEKEN_NEW) int i; AP_IF_S* ar; uint32_t num; bk_printf("Scan begin...\r\n"); tuya_hal_wifi_all_ap_scan(&ar, &num); bk_printf("Scan returned %i networks\r\n", num); for (i = 0; i < num; i++) { hprintf255(request, "[%i/%i] SSID: %s, Channel: %i, Signal %i
", i, (int)num, ar[i].ssid, ar[i].channel, ar[i].rssi); } tuya_hal_wifi_release_ap(ar); #elif defined(PLATFORM_BK7231N) && !defined(PLATFORM_BEKEN_NEW) int i; AP_IF_S* ar; uint32_t num; bk_printf("Scan begin...\r\n"); tuya_os_adapt_wifi_all_ap_scan(&ar, (unsigned int*)&num); bk_printf("Scan returned %i networks\r\n", num); for (i = 0; i < num; i++) { hprintf255(request, "[%i/%i] SSID: %s, Channel: %i, Signal %i
", i + 1, (int)num, ar[i].ssid, ar[i].channel, ar[i].rssi); } tuya_os_adapt_wifi_release_ap(ar); #elif PLATFORM_ESPIDF || PLATFORM_ESP8266 // doesn't work in ap mode, only sta/apsta uint16_t ap_count = 0, number = 30; wifi_ap_record_t ap_info[number]; memset(ap_info, 0, sizeof(ap_info)); bk_printf("Scan begin...\r\n"); esp_wifi_scan_start(NULL, true); esp_wifi_scan_get_ap_num(&ap_count); bk_printf("Scan returned %i networks, max allowed: %i\r\n", ap_count, number); esp_wifi_scan_get_ap_records(&number, ap_info); for(int i = 0; i < number; i++) { hprintf255(request, "[%i/%u] SSID: %s, Channel: %i, Signal %i
", i + 1, number, ap_info[i].ssid, ap_info[i].primary, ap_info[i].rssi); } #elif defined(PLATFORM_REALTEK) && !PLATFORM_REALTEK_NEW #ifndef PLATFORM_RTL87X0C extern void rltk_wlan_enable_scan_with_ssid_by_extended_security(bool); #endif rtw_result_t scan_result_handler(rtw_scan_handler_result_t* result) { http_request_t* request = (http_request_t*)result->user_data; if(result->scan_complete == RTW_TRUE) { xSemaphoreGive(scan_hdl); return RTW_SUCCESS; } rtw_scan_result_t* record = &result->ap_details; record->SSID.val[record->SSID.len] = 0; hprintf255(request, "SSID: %s, Channel: %i, Signal %i
", record->SSID.val, record->channel, record->signal_strength); return 0; } scan_hdl = xSemaphoreCreateBinary(); rltk_wlan_enable_scan_with_ssid_by_extended_security(1); xSemaphoreTake(scan_hdl, 1); if(wifi_scan_networks(scan_result_handler, request) != RTW_SUCCESS) { poststr(request, "Wifi scan failed!
"); } xSemaphoreTake(scan_hdl, pdMS_TO_TICKS(10 * 1000)); vSemaphoreDelete(scan_hdl); #else hprintf255(request, "TODO %s
", PLATFORM_MCU_NAME); #endif } poststr(request, "
\ \ \
"); poststr_h4(request, "Use this to disconnect from your WiFi"); poststr(request, "
\ \ \
"); poststr_h2(request, "Use this to connect to your WiFi"); add_label_text_field(request, "SSID", "ssid", CFG_GetWiFiSSID(), "
"); add_label_password_field(request, "", "pass", CFG_GetWiFiPass(), "
Password enable clear text password (clears existing)"); poststr_h2(request, "Alternate WiFi (used when first one is not responding)"); poststr(request, "Note: It is possible to retain used SSID using command setStartupSSIDChannel in early.bat"); #ifndef PLATFORM_BEKEN poststr_h2(request, "SSID2 only on Beken Platform (BK7231T, BK7231N)"); #endif add_label_text_field(request, "SSID2", "ssid2", CFG_GetWiFiSSID2(), ""); add_label_password_field(request, "", "pass2", CFG_GetWiFiPass2(), "
Password2 enable clear text password (clears existing)"); #if ALLOW_WEB_PASSWORD int web_password_enabled = strcmp(CFG_GetWebPassword(), "") == 0 ? 0 : 1; poststr_h2(request, "Web Authentication"); poststr(request, "

Enabling web authentication will protect this web interface and API using basic HTTP authentication. Username is always admin.

"); hprintf255(request, "
", (web_password_enabled > 0 ? " checked" : "")); poststr(request, "
"); add_label_password_field(request, "Admin Password", "web_admin_password", CFG_GetWebPassword(), ""); #endif poststr(request, "

\ \
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #if ENABLE_HTTP_NAMES int http_fn_cfg_name(http_request_t* request) { // for a test, show password as well... char tmpA[128]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set name"); poststr_h2(request, "Change device names for display"); if (http_getArg(request->url, "shortName", tmpA, sizeof(tmpA))) { if (STR_ReplaceWhiteSpacesWithUnderscore(tmpA)) { poststr_h2(request, "You cannot have whitespaces in short name!"); } CFG_SetShortDeviceName(tmpA); } if (http_getArg(request->url, "name", tmpA, sizeof(tmpA))) { CFG_SetDeviceName(tmpA); } CFG_Save_IfThereArePendingChanges(); poststr_h2(request, "Use this to change device names"); add_label_name_field(request, "ShortName", "shortName", CFG_GetShortDeviceName(), "
"); add_label_name_field(request, "Full Name", "name", CFG_GetDeviceName(), "
"); poststr(request, "

"); poststr(request, ""); poststr(request, "
"); //poststr(request,htmlReturnToCfg); //HTTP_AddBuildFooter(request); //poststr(request,htmlEnd); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif int http_fn_cfg_wifi_set(http_request_t* request) { char tmpA[128]; int bChanged; addLogAdv(LOG_INFO, LOG_FEATURE_HTTP, "HTTP_ProcessPacket: generating cfg_wifi_set \r\n"); bChanged = 0; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Saving Wifi"); if (http_getArg(request->url, "open", tmpA, sizeof(tmpA))) { bChanged |= CFG_SetWiFiSSID(""); bChanged |= CFG_SetWiFiPass(""); poststr(request, "WiFi mode set: open access point."); } else { if (http_getArg(request->url, "ssid", tmpA, sizeof(tmpA))) { bChanged |= CFG_SetWiFiSSID(tmpA); } if (http_getArg(request->url, "pass", tmpA, sizeof(tmpA))) { bChanged |= CFG_SetWiFiPass(tmpA); } poststr(request, "WiFi mode set: connect to WLAN."); if(bChanged) HAL_DisableEnhancedFastConnect(); } if (http_getArg(request->url, "ssid2", tmpA, sizeof(tmpA))) { bChanged |= CFG_SetWiFiSSID2(tmpA); } if (http_getArg(request->url, "pass2", tmpA, sizeof(tmpA))) { bChanged |= CFG_SetWiFiPass2(tmpA); } #if ALLOW_WEB_PASSWORD if (http_getArg(request->url, "web_admin_password_enabled", tmpA, sizeof(tmpA))) { int web_password_enabled = atoi(tmpA); if (web_password_enabled > 0 && http_getArg(request->url, "web_admin_password", tmpA, sizeof(tmpA))) { if (strlen(tmpA) < 5) { poststr_h4(request, "Web password needs to be at least 5 characters long!"); } else { poststr(request, "

Web password has been changed.

"); CFG_SetWebPassword(tmpA); } } } else { CFG_SetWebPassword(""); } #endif CFG_Save_SetupTimer(); if (bChanged == 0) { poststr(request, "

WiFi: No changes detected.

"); } else { poststr(request, "

WiFi: Please wait for module to reset...

"); RESET_ScheduleModuleReset(3); } poststr(request, "
Return to WiFi settings
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } int http_fn_cfg_loglevel_set(http_request_t* request) { char tmpA[128]; addLogAdv(LOG_INFO, LOG_FEATURE_HTTP, "HTTP_ProcessPacket: generating cfg_loglevel_set \r\n"); http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set log level"); if (http_getArg(request->url, "loglevel", tmpA, sizeof(tmpA))) { #if WINDOWS #else g_loglevel = atoi(tmpA); #endif poststr(request, "LOG level changed."); } tmpA[0] = 0; #if WINDOWS add_label_text_field(request, "Loglevel", "loglevel", "", "
"); #else add_label_numeric_field(request, "Loglevel", "loglevel", g_loglevel, ""); #endif poststr(request, "

\ \
"); poststr(request, "
Return to config settings
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #if ENABLE_HTTP_MAC int http_fn_cfg_mac(http_request_t* request) { // must be unsigned, else print below prints negatives as e.g. FFFFFFFe unsigned char mac[6]; char tmpA[128]; int i; char macStr[16]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set MAC address"); if (http_getArg(request->url, "mac", tmpA, sizeof(tmpA))) { for (i = 0; i < 6; i++) { mac[i] = hexbyte(&tmpA[i * 2]); } //sscanf(tmpA,"%02X%02X%02X%02X%02X%02X",&mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]); if (WiFI_SetMacAddress((char*)mac)) { poststr_h4(request, "New MAC set!"); } else { poststr_h4(request, "MAC change error?"); } CFG_Save_IfThereArePendingChanges(); } WiFI_GetMacAddress((char*)mac); poststr_h2(request, "Here you can change MAC address."); sprintf(macStr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); add_label_text_field(request, "MAC", "mac", macStr, "
"); poststr(request, "

\ \
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif const char* CMD_GetResultString(commandResult_t r) { if (r == CMD_RES_OK) return "OK"; if (r == CMD_RES_EMPTY_STRING) return "No command entered"; if (r == CMD_RES_ERROR) return "Command found but returned error"; if (r == CMD_RES_NOT_ENOUGH_ARGUMENTS) return "Not enough arguments for this command"; if (r == CMD_RES_UNKNOWN_COMMAND) return "Unknown command"; if (r == CMD_RES_BAD_ARGUMENT) return "Bad argument"; return "Unknown error"; } // all log printfs made by command will be sent also to request void LOG_SetCommandHTTPRedirectReply(http_request_t* request); int http_fn_cmd_tool(http_request_t* request) { commandResult_t res; const char* resStr; char tmpA[128]; char* long_str_alloced = 0; int commandLen; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Command tool"); poststr_h4(request, "Command Tool"); poststr(request, "This is a basic command line.
"); poststr(request, "Please consider using the 'Web Application' console for more options and real-time log viewing.
"); poststr(request, "Remember that some commands are added after a restart when a driver is activated.
"); commandLen = http_getArg(request->url, "cmd", tmpA, sizeof(tmpA)); addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "http_fn_cmd_tool: len %i",commandLen); if (commandLen) { poststr(request, "
"); // all log printfs made by command will be sent also to request LOG_SetCommandHTTPRedirectReply(request); if (commandLen > (sizeof(tmpA) - 5)) { commandLen += 8; long_str_alloced = (char*)malloc(commandLen); if (long_str_alloced) { http_getArg(request->url, "cmd", long_str_alloced, commandLen); res = CMD_ExecuteCommand(long_str_alloced, COMMAND_FLAG_SOURCE_CONSOLE); free(long_str_alloced); } else { res = CMD_RES_ERROR; } } else { res = CMD_ExecuteCommand(tmpA, COMMAND_FLAG_SOURCE_CONSOLE); } LOG_SetCommandHTTPRedirectReply(0); resStr = CMD_GetResultString(res); hprintf255(request, "

%s

", resStr); poststr(request, "
"); } add_label_text_field(request, "Command", "cmd", tmpA, "
"); poststr(request, SUBMIT_AND_END_FORM); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #if ENABLE_HTTP_STARTUP int http_fn_startup_command(http_request_t* request) { char tmpA[8]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Set startup command"); poststr_h4(request, "Set/Change/Clear startup command line"); poststr(request, "

Startup command is a shorter, smaller alternative to LittleFS autoexec.bat. " "The startup commands are run at device startup. " "You can use them to init peripherals and drivers, like BL0942 energy sensor. " "Use backlog cmd1; cmd2; cmd3; etc to enter multiple commands

"); if (http_getArg(request->url, "startup_cmd", tmpA, sizeof(tmpA))) { // direct config access to remove buffer on stack int realSize = http_getArg(request->url, "data", g_cfg.initCommandLine, sizeof(g_cfg.initCommandLine)); // mark as dirty (value has changed) g_cfg_pendingChanges++; if (realSize >= sizeof(g_cfg.initCommandLine)) { hprintf255(request, "

Command trimmed from %i to %i!

",realSize, sizeof(g_cfg.initCommandLine)); } else { hprintf255(request, "

Command changed!

"); } CFG_Save_IfThereArePendingChanges(); } #if ENABLE_OBK_SCRIPTING poststr(request, ""); poststr(request, "
"); poststr(request, "
"); #else add_label_text_field(request, "Startup command", "data", CFG_GetShortStartupCommand(), ""); #endif poststr(request, ""); poststr(request, SUBMIT_AND_END_FORM); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif #if ENABLE_HA_DISCOVERY HassDeviceInfo *hass_createEnumChannelInfo(int i) { HassDeviceInfo *dev_info = 0; channelEnum_t *en; if (g_enums != NULL && g_enums[i]->numOptions != 0) { en = g_enums[i]; } else { // revert to textfield if no enums are defined dev_info = hass_init_textField_info(i); return dev_info;; } char **options = (char**)malloc(en->numOptions * sizeof(char *)); for (int o = 0; o < en->numOptions; o++) { options[o] = en->options[o].label; } if (en->options != NULL && en->numOptions > 0) { // backlog setChannelType 1 Enum; setChannelEnum 0:red 2:blue 3:green; scheduleHADiscovery 1 char stateTopic[32]; char cmdTopic[32]; char title[64]; char value_tmp[1024]; char command_tmp[1024]; CMD_GenEnumValueTemplate(en, value_tmp, sizeof(value_tmp)); CMD_GenEnumCommandTemplate(en, command_tmp, sizeof(command_tmp)); strcpy(title, CHANNEL_GetLabel(i)); sprintf(stateTopic, "~/%i/get", i); sprintf(cmdTopic, "~/%i/set", i); dev_info = hass_createSelectEntityIndexedCustom( stateTopic, cmdTopic, en->numOptions, (const char**)options, title, value_tmp, command_tmp ); } os_free(options); return dev_info; } void doHomeAssistantDiscovery(const char* topic, http_request_t* request) { int i; int relayCount; int pwmCount; int dInputCount; int excludedCount = 0; bool ledDriverChipRunning; HassDeviceInfo* dev_info = NULL; bool measuringPower = false; bool measuringBattery = false; struct cJSON_Hooks hooks; bool discoveryQueued = false; int type; // warning - this is 32 bit int flagsChannelPublished; int ch; int dimmer, toggle, brightness_scale = 0; // no channels published yet flagsChannelPublished = 0; for (i = 0; i < CHANNEL_MAX; i++) { if (CHANNEL_HasNeverPublishFlag(i)) { BIT_SET(flagsChannelPublished, i); excludedCount++; } } if (topic == 0 || *topic == 0) { topic = "homeassistant"; } #ifdef ENABLE_DRIVER_BL0937 measuringPower = DRV_IsMeasuringPower(); #endif measuringBattery = DRV_IsMeasuringBattery(); PIN_get_Relay_PWM_Count(&relayCount, &pwmCount, &dInputCount); addLogAdv(LOG_INFO, LOG_FEATURE_HTTP, "HASS counts: %i rels, %i pwms, %i inps, %i excluded", relayCount, pwmCount, dInputCount, excludedCount); #if ENABLE_LED_BASIC ledDriverChipRunning = LED_IsLedDriverChipRunning(); #else ledDriverChipRunning = 0; #endif #if PLATFORM_TXW81X hooks.malloc_fn = _os_malloc; hooks.free_fn = _os_free; #else hooks.malloc_fn = os_malloc; hooks.free_fn = os_free; #endif cJSON_InitHooks(&hooks); DRV_OnHassDiscovery(topic); EventHandlers_FireEvent(CMD_EVENT_ON_DISCOVERY, 0); #if ENABLE_ADVANCED_CHANNELTYPES_DISCOVERY // try to pair toggles with dimmers. This is needed only for TuyaMCU, // where custom channel types are used. This is NOT used for simple // CW/RGB/RGBCW/etc lights. if (CFG_HasFlag(OBK_FLAG_DISCOVERY_DONT_MERGE_LIGHTS) == false) { while (true) { // find first dimmer dimmer = -1; for (i = 0; i < CHANNEL_MAX; i++) { type = g_cfg.pins.channelTypes[i]; if (BIT_CHECK(flagsChannelPublished, i)) { continue; } if (type == ChType_Dimmer) { brightness_scale = 100; dimmer = i; break; } if (type == ChType_Dimmer1000) { brightness_scale = 1000; dimmer = i; break; } if (type == ChType_Dimmer256) { brightness_scale = 256; dimmer = i; break; } } // find first togle toggle = -1; for (i = 0; i < CHANNEL_MAX; i++) { type = g_cfg.pins.channelTypes[i]; if (BIT_CHECK(flagsChannelPublished, i)) { continue; } if (type == ChType_Toggle) { toggle = i; break; } } // if nothing found, stop if (toggle == -1 || dimmer == -1) { break; } BIT_SET(flagsChannelPublished, toggle); BIT_SET(flagsChannelPublished, dimmer); dev_info = hass_init_light_singleColor_onChannels(toggle, dimmer, brightness_scale); 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 #if ENABLE_LED_BASIC if (ledDriverChipRunning) { pwmCount = CFG_CountLEDRemapChannels(); } if (pwmCount == 5 || (pwmCount == 4 && CFG_HasFlag(OBK_FLAG_LED_EMULATE_COOL_WITH_RGB))) { if (dev_info == NULL) { dev_info = hass_init_light_device_info(LIGHT_RGBCW); } // Enable + RGB control + CW control MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = NULL; discoveryQueued = true; } else if (pwmCount > 0) { if (pwmCount == 4) { addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "4 PWM device not yet handled\r\n"); } else if (pwmCount == 3) { // Enable + RGB control dev_info = hass_init_light_device_info(LIGHT_RGB); } else if (pwmCount == 2) { // PWM + Temperature (https://github.com/openshwprojects/OpenBK7231T_App/issues/279) dev_info = hass_init_light_device_info(LIGHT_PWMCW); } else { dev_info = hass_init_light_device_info(LIGHT_PWM); } if (dev_info != NULL) { MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = NULL; discoveryQueued = true; } } #endif #ifdef ENABLE_DRIVER_BL0937 if (measuringPower == true) { for (i = OBK__FIRST; i <= OBK__LAST; 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 (i == OBK_VOLTAGE) { //20250319 XJIKKA to simplify and save space in flash frequency together with voltage dev_info = hass_init_sensor_device_info(FREQUENCY_SENSOR, SPECIAL_CHANNEL_OBK_FREQUENCY, -1, -1, -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; } } } #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++) { 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 if (measuringBattery == true) { dev_info = hass_init_sensor_device_info(BATTERY_SENSOR, 0, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = hass_init_sensor_device_info(BATTERY_VOLTAGE_SENSOR, 0, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); discoveryQueued = true; } for (i = 0; i < PLATFORM_GPIO_MAX; i++) { if (IS_PIN_DHT_ROLE(g_cfg.pins.roles[i]) || IS_PIN_TEMP_HUM_SENSOR_ROLE(g_cfg.pins.roles[i])) { ch = PIN_GetPinChannelForPinIndex(i); // TODO: flags are 32 bit and there are 64 max channels BIT_SET(flagsChannelPublished, ch); dev_info = hass_init_sensor_device_info(TEMPERATURE_SENSOR, ch, 2, 1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); ch = PIN_GetPinChannel2ForPinIndex(i); // TODO: flags are 32 bit and there are 64 max channels BIT_SET(flagsChannelPublished, ch); dev_info = hass_init_sensor_device_info(HUMIDITY_SENSOR, ch, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); discoveryQueued = true; } else if (IS_PIN_AIR_SENSOR_ROLE(g_cfg.pins.roles[i])) { ch = PIN_GetPinChannelForPinIndex(i); // TODO: flags are 32 bit and there are 64 max channels BIT_SET(flagsChannelPublished, ch); dev_info = hass_init_sensor_device_info(CO2_SENSOR, ch, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); ch = PIN_GetPinChannel2ForPinIndex(i); // TODO: flags are 32 bit and there are 64 max channels BIT_SET(flagsChannelPublished, ch); dev_info = hass_init_sensor_device_info(TVOC_SENSOR, ch, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); discoveryQueued = true; } } //{ // HassDeviceInfo*dev_info = hass_createGarageEntity("~/1/get", "~/1/set", // "Main Door"); // 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_ADVANCED_CHANNELTYPES_DISCOVERY for (i = 0; i < CHANNEL_MAX; i++) { type = g_cfg.pins.channelTypes[i]; // TODO: flags are 32 bit and there are 64 max channels if (BIT_CHECK(flagsChannelPublished, i)) { continue; } dev_info = 0; switch (type) { case ChType_Motion: { dev_info = hass_init_binary_sensor_device_info(i, true); cJSON_AddStringToObject(dev_info->root, "dev_cla", "motion"); } break; case ChType_Motion_n: { dev_info = hass_init_binary_sensor_device_info(i, false); cJSON_AddStringToObject(dev_info->root, "dev_cla", "motion"); } break; case ChType_OpenClosed: { dev_info = hass_init_binary_sensor_device_info(i, false); } break; case ChType_OpenClosed_Inv: { dev_info = hass_init_binary_sensor_device_info(i, true); } break; case ChType_Voltage_div10: { dev_info = hass_init_sensor_device_info(VOLTAGE_SENSOR, i, 2, 1, 1); } break; case ChType_Voltage_div100: { dev_info = hass_init_sensor_device_info(VOLTAGE_SENSOR, i, 2, 2, 1); } break; case ChType_ReadOnlyLowMidHigh: { dev_info = hass_init_sensor_device_info(READONLYLOWMIDHIGH_SENSOR, i, -1, -1, 1); } break; case ChType_BatteryLevelPercent: { dev_info = hass_init_sensor_device_info(BATTERY_CHANNEL_SENSOR, i, -1, -1, 1); } break; case ChType_SmokePercent: { dev_info = hass_init_sensor_device_info(SMOKE_SENSOR, i, -1, -1, 1); } break; case ChType_Illuminance: { dev_info = hass_init_sensor_device_info(ILLUMINANCE_SENSOR, i, -1, -1, 1); } break; case ChType_Custom: case ChType_ReadOnly: { dev_info = hass_init_sensor_device_info(CUSTOM_SENSOR, i, -1, -1, 1); } break; case ChType_Temperature: { dev_info = hass_init_sensor_device_info(TEMPERATURE_SENSOR, i, -1, -1, 1); } break; case ChType_Temperature_div2: { dev_info = hass_init_sensor_device_info(TEMPERATURE_SENSOR, i, 2, 1, 5); } break; case ChType_Temperature_div10: { dev_info = hass_init_sensor_device_info(TEMPERATURE_SENSOR, i, 2, 1, 1); } break; case ChType_ReadOnly_div10: { dev_info = hass_init_sensor_device_info(CUSTOM_SENSOR, i, 2, 1, 1); } break; case ChType_Temperature_div100: { dev_info = hass_init_sensor_device_info(TEMPERATURE_SENSOR, i, 2, 2, 1); } break; case ChType_ReadOnly_div100: { dev_info = hass_init_sensor_device_info(CUSTOM_SENSOR, i, 2, 2, 1); } break; case ChType_Humidity: { dev_info = hass_init_sensor_device_info(HUMIDITY_SENSOR, i, -1, -1, 1); } break; case ChType_Humidity_div10: { dev_info = hass_init_sensor_device_info(HUMIDITY_SENSOR, i, 2, 1, 1); } break; case ChType_Current_div100: { dev_info = hass_init_sensor_device_info(CURRENT_SENSOR, i, 3, 2, 1); } break; case ChType_ReadOnly_div1000: { dev_info = hass_init_sensor_device_info(CUSTOM_SENSOR, i, 3, 3, 1); } break; case ChType_LeakageCurrent_div1000: case ChType_Current_div1000: { dev_info = hass_init_sensor_device_info(CURRENT_SENSOR, i, 3, 3, 1); } break; case ChType_Power: { dev_info = hass_init_sensor_device_info(POWER_SENSOR, i, -1, -1, 1); } break; case ChType_Power_div10: { dev_info = hass_init_sensor_device_info(POWER_SENSOR, i, 2, 1, 1); } break; case ChType_Power_div100: { dev_info = hass_init_sensor_device_info(POWER_SENSOR, i, 3, 2, 1); } break; case ChType_PowerFactor_div100: { dev_info = hass_init_sensor_device_info(POWERFACTOR_SENSOR, i, 3, 2, 1); } break; case ChType_Pressure_div100: { dev_info = hass_init_sensor_device_info(PRESSURE_SENSOR, i, 3, 2, 1); } break; case ChType_PowerFactor_div1000: { dev_info = hass_init_sensor_device_info(POWERFACTOR_SENSOR, i, 4, 3, 1); } break; case ChType_Frequency_div100: { dev_info = hass_init_sensor_device_info(FREQUENCY_SENSOR, i, 3, 2, 1); } break; case ChType_Percent: { dev_info = hass_init_sensor_device_info(HASS_PERCENT, i, 3, 2, 1); } break; case ChType_Frequency_div1000: { dev_info = hass_init_sensor_device_info(FREQUENCY_SENSOR, i, 4, 3, 1); } break; case ChType_Frequency_div10: { dev_info = hass_init_sensor_device_info(FREQUENCY_SENSOR, i, 3, 1, 1); } break; case ChType_EnergyTotal_kWh_div100: { dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 2, 1); } break; case ChType_EnergyExport_kWh_div1000: { dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 3, 1); } break; case ChType_EnergyImport_kWh_div1000: { dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 3, 1); } break; case ChType_EnergyTotal_kWh_div1000: { dev_info = hass_init_sensor_device_info(ENERGY_SENSOR, i, 3, 3, 1); } break; case ChType_Ph: { dev_info = hass_init_sensor_device_info(WATER_QUALITY_PH, i, 2, 2, 1); } break; case ChType_Orp: { dev_info = hass_init_sensor_device_info(WATER_QUALITY_ORP, i, -1, 2, 1); } break; case ChType_Tds: { dev_info = hass_init_sensor_device_info(WATER_QUALITY_TDS, i, -1, 2, 1); } break; case ChType_TextField: { dev_info = hass_init_textField_info(i); } break; case ChType_ReadOnlyEnum: { dev_info = hass_init_sensor_device_info(HASS_READONLYENUM, i, -1, -1, -1); } break; case ChType_Enum: { dev_info = hass_createEnumChannelInfo(i); } break; default: { int numOptions; const char **options = Channel_GetOptionsForChannelType(type, &numOptions); if (options && numOptions) { // backlog setChannelType 2 LowMidHigh; scheduleHADiscovery 1 // backlog setChannelType 3 OpenStopClose; scheduleHADiscovery 1 char stateTopic[16]; char cmdTopic[16]; // TODO: lengths sprintf(stateTopic, "~/%i/get", i); sprintf(cmdTopic, "~/%i/set", i); dev_info = hass_createSelectEntityIndexed( stateTopic, cmdTopic, numOptions, options, CHANNEL_GetLabel(i) ); } } break; } 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); BIT_SET(flagsChannelPublished, i); discoveryQueued = true; } } #endif //if (relayCount > 0) { for (i = 0; i < CHANNEL_MAX; i++) { // if already included by light, skip if (BIT_CHECK(flagsChannelPublished, i)) { continue; } bool bToggleInv = g_cfg.pins.channelTypes[i] == ChType_Toggle_Inv; if (h_isChannelRelay(i) || g_cfg.pins.channelTypes[i] == ChType_Toggle || bToggleInv) { // TODO: flags are 32 bit and there are 64 max channels BIT_SET(flagsChannelPublished, i); if (CFG_HasFlag(OBK_FLAG_MQTT_HASS_ADD_RELAYS_AS_LIGHTS)) { dev_info = hass_init_relay_device_info(i, LIGHT_ON_OFF, bToggleInv); } else { dev_info = hass_init_relay_device_info(i, RELAY, bToggleInv); } MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = NULL; discoveryQueued = true; } } //} if (dInputCount > 0) { for (i = 0; i < CHANNEL_MAX; i++) { if (h_isChannelDigitalInput(i)) { if (BIT_CHECK(flagsChannelPublished, i)) { continue; } // TODO: flags are 32 bit and there are 64 max channels BIT_SET(flagsChannelPublished, i); dev_info = hass_init_binary_sensor_device_info(i, false); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = NULL; discoveryQueued = true; } } } if(CFG_HasFlag(OBK_FLAG_MQTT_BROADCASTSELFSTATEPERMINUTE) || CFG_HasFlag(OBK_FLAG_MQTT_BROADCASTSELFSTATEONCONNECT)) { //use -1 for channel as these don't correspond to channels #ifndef NO_CHIP_TEMPERATURE dev_info = hass_init_sensor_device_info(HASS_TEMP, -1, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); #endif dev_info = hass_init_sensor_device_info(HASS_RSSI, -1, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = hass_init_sensor_device_info(HASS_UPTIME, -1, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = hass_init_sensor_device_info(HASS_BUILD, -1, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = hass_init_sensor_device_info(HASS_SSID, -1, -1, -1, 1); MQTT_QueuePublish(topic, dev_info->channel, hass_build_discovery_json(dev_info), OBK_PUBLISH_FLAG_RETAIN); hass_free_device_info(dev_info); dev_info = hass_init_sensor_device_info(HASS_IP, -1, -1, -1, 1); 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 (discoveryQueued) { MQTT_InvokeCommandAtEnd(PublishChannels); } else { const char* msg = "No relay, PWM, sensor or power driver running."; if (request) { poststr(request, msg); poststr(request, NULL); } else { addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "HA discovery: %s\r\n", msg); } } } /// @brief Sends HomeAssistant discovery MQTT messages. /// @param request /// @return int http_fn_ha_discovery(http_request_t* request) { char topic[32]; http_setup(request, httpMimeTypeText); if (MQTT_IsReady() == false) { poststr(request, "MQTT not running."); poststr(request, NULL); return 0; } // even if it returns the empty HA topic, // the function call below will set default http_getArg(request->url, "prefix", topic, sizeof(topic)); doHomeAssistantDiscovery(topic, request); poststr(request, "MQTT discovery queued."); poststr(request, NULL); return 0; } #endif #if ENABLE_OLD_YAML_GENERATOR void http_generate_singleColor_cfg(http_request_t* request, const char* clientId) { hprintf255(request, " command_topic: \"cmnd/%s/led_enableAll\"\n", clientId); hprintf255(request, " state_topic: \"%s/led_enableAll/get\"\n", clientId); hprintf255(request, " availability_topic: \"%s/connected\"\n", clientId); hprintf255(request, " payload_on: 1\n"); hprintf255(request, " payload_off: 0\n"); hprintf255(request, " brightness_command_topic: \"cmnd/%s/led_dimmer\"\n", clientId); hprintf255(request, " brightness_state_topic: \"%s/led_dimmer/get\"\n", clientId); hprintf255(request, " brightness_scale: 100\n"); } void http_generate_rgb_cfg(http_request_t* request, const char* clientId) { hprintf255(request, " rgb_command_template: \"{{ '#%%02x%%02x%%02x0000' | format(red, green, blue)}}\"\n"); hprintf255(request, " rgb_value_template: \"{{ value[0:2]|int(base=16) }},{{ value[2:4]|int(base=16) }},{{ value[4:6]|int(base=16) }}\"\n"); hprintf255(request, " rgb_state_topic: \"%s/led_basecolor_rgb/get\"\n", clientId); hprintf255(request, " rgb_command_topic: \"cmnd/%s/led_basecolor_rgb\"\n", clientId); http_generate_singleColor_cfg(request, clientId); } void http_generate_cw_cfg(http_request_t* request, const char* clientId) { hprintf255(request, " color_temp_command_topic: \"cmnd/%s/led_temperature\"\n", clientId); hprintf255(request, " color_temp_state_topic: \"%s/led_temperature/get\"\n", clientId); http_generate_singleColor_cfg(request, clientId); } void hprintf_qos_payload(http_request_t* request, const char* clientId) { poststr(request, " qos: 1\n"); poststr(request, " payload_on: 1\n"); poststr(request, " payload_off: 0\n"); poststr(request, " retain: true\n"); hprintf255(request, " availability:\n"); hprintf255(request, " - topic: \"%s/connected\"\n", clientId); } #endif #if ENABLE_HA_DISCOVERY int http_fn_ha_cfg(http_request_t* request) { int relayCount; int pwmCount; int dInputCount; const char* shortDeviceName; const char* clientId; int i; char mqttAdded = 0; char switchAdded = 0; char lightAdded = 0; i = 0; shortDeviceName = CFG_GetShortDeviceName(); clientId = CFG_GetMQTTClientId(); http_setup(request, httpMimeTypeHTML); http_html_start(request, "Home Assistant Setup"); poststr_h4(request, "Home Assistant Cfg"); hprintf255(request, "

Note that your short device name is: %s

", shortDeviceName); #if ENABLE_OLD_YAML_GENERATOR poststr_h4(request, "Paste this to configuration yaml"); poststr(request, "
Make sure that you have \"switch:\" keyword only once! Home Assistant doesn't like dup keywords.
"); poststr(request, "
You can also use \"switch MyDeviceName:\" to avoid keyword duplication!
"); poststr(request, ""); #endif poststr(request, "
 

"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, ha_discovery_script); poststr(request, NULL); return 0; } #endif void runHTTPCommandInternal(http_request_t* request, const char *cmd) { bool bEchoHack = strncmp(cmd, "echo", 4) == 0; CMD_ExecuteCommand(cmd, COMMAND_FLAG_SOURCE_HTTP); #if ENABLE_TASMOTA_JSON if (!bEchoHack) { JSON_ProcessCommandReply(cmd, skipToNextWord(cmd), request, (jsonCb_t)hprintf255, COMMAND_FLAG_SOURCE_HTTP); } else { const char *s = Tokenizer_GetArg(0); poststr(request, s); } #endif } int http_fn_cm(http_request_t* request) { char tmpA[128]; char* long_str_alloced = 0; int commandLen = 0; http_setup(request, httpMimeTypeJson); // exec command if (request->method == HTTP_GET) { commandLen = http_getArg(request->url, "cmnd", tmpA, sizeof(tmpA)); //ADDLOG_INFO(LOG_FEATURE_HTTP, "Got here (GET) %s;%s;%d\n", request->url, tmpA, commandLen); } else if (request->method == HTTP_POST || request->method == HTTP_PUT) { commandLen = http_getRawArg(request->bodystart, "cmnd", tmpA, sizeof(tmpA)); //ADDLOG_INFO(LOG_FEATURE_HTTP, "Got here (POST) %s;%s;%d\n", request->bodystart, tmpA, commandLen); } if (commandLen) { if (commandLen > (sizeof(tmpA) - 5)) { commandLen += 8; long_str_alloced = (char*)malloc(commandLen); if (long_str_alloced) { if (request->method == HTTP_GET) { http_getArg(request->url, "cmnd", long_str_alloced, commandLen); } else if (request->method == HTTP_POST || request->method == HTTP_PUT) { http_getRawArg(request->bodystart, "cmnd", long_str_alloced, commandLen); } CMD_ExecuteCommand(long_str_alloced, COMMAND_FLAG_SOURCE_HTTP); runHTTPCommandInternal(request, long_str_alloced); free(long_str_alloced); } } else { runHTTPCommandInternal(request, tmpA); } } poststr(request, NULL); return 0; } int http_fn_cfg(http_request_t* request) { http_setup(request, httpMimeTypeHTML); http_html_start(request, "Config"); postFormAction(request, "cfg_pins", "Configure Module"); #if ENABLE_HTTP_FLAGS postFormAction(request, "cfg_generic", "Configure General/Flags"); #endif #if ENABLE_HTTP_STARTUP postFormAction(request, "cfg_startup", "Configure Startup"); #endif #if ENABLE_HTTP_DGR postFormAction(request, "cfg_dgr", "Configure Device Groups"); #endif postFormAction(request, "cfg_wifi", "Configure WiFi & Web"); #if ENABLE_HTTP_IP postFormAction(request, "cfg_ip", "Configure IP"); #endif #if (ENABLE_DRIVER_DS1820_FULL) postFormAction(request, "cfg_ds18b20", "Configure DS18B20 Sensors"); #endif postFormAction(request, "cfg_mqtt", "Configure MQTT"); #if ENABLE_HTTP_NAMES postFormAction(request, "cfg_name", "Configure Names"); #endif #if ENABLE_HTTP_MAC postFormAction(request, "cfg_mac", "Change MAC"); #endif #if ENABLE_HTTP_PING postFormAction(request, "cfg_ping", "Ping Watchdog (network lost restarter)"); #endif #if ENABLE_HTTP_WEBAPP postFormAction(request, "cfg_webapp", "Configure WebApp"); #endif #if ENABLE_HA_DISCOVERY postFormAction(request, "ha_cfg", "Home Assistant Configuration"); #endif postFormAction(request, "ota", "OTA (update software by WiFi)"); postFormAction(request, "cmd_tool", "Execute Custom Command"); #if ENABLE_HTTP_STARTUP postFormAction(request, "startup_command", "Change Startup Command Text"); #endif #if 0 #if PLATFORM_BK7231T | PLATFORM_BK7231N { int i, j, k; k = config_get_tableOffsets(BK_PARTITION_NET_PARAM, &i, &j); hprintf255(request, "BK_PARTITION_NET_PARAM: bOk %i, at %i, len %i
", k, i, j); k = config_get_tableOffsets(BK_PARTITION_RF_FIRMWARE, &i, &j); hprintf255(request, "BK_PARTITION_RF_FIRMWARE: bOk %i, at %i, len %i
", k, i, j); k = config_get_tableOffsets(BK_PARTITION_OTA, &i, &j); hprintf255(request, "BK_PARTITION_OTA: bOk %i, at %i, len %i
", k, i, j); } #endif #endif poststr(request, htmlFooterReturnToMainPage); http_html_end(request); poststr(request, NULL); return 0; } int http_fn_cfg_pins(http_request_t* request) { int iChanged = 0; int iChangedRequested = 0; int i; char tmpA[128]; char tmpB[64]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Pin config"); #if 0 poststr(request, ""); //poststr(request, ""); poststr(request, ""); #endif poststr(request, "

The first field assigns a role to the given pin. The next field is used to enter channel index (relay index), used to support multiple relays and buttons. "); poststr(request, "So, first button and first relay should have channel 1, second button and second relay have channel 2, etc.

"); poststr(request, "

Only for button roles another field will be provided to enter channel to toggle when doing double click. "); poststr(request, "It shows up when you change role to button.

"); #if PLATFORM_BK7231N || PLATFORM_BK7231T poststr(request, "

BK7231N/BK7231T supports PWM only on pins 6, 7, 8, 9, 24 and 26!

"); #endif for (i = 0; i < PLATFORM_GPIO_MAX; i++) { sprintf(tmpA, "%i", i); if (http_getArg(request->url, tmpA, tmpB, sizeof(tmpB))) { int role; int pr; iChangedRequested++; role = atoi(tmpB); pr = PIN_GetPinRoleForPinIndex(i); if (pr != role) { PIN_SetPinRoleForPinIndex(i, role); iChanged++; } } sprintf(tmpA, "r%i", i); if (http_getArg(request->url, tmpA, tmpB, sizeof(tmpB))) { int rel; int prevRel; iChangedRequested++; rel = atoi(tmpB); prevRel = PIN_GetPinChannelForPinIndex(i); if (prevRel != rel) { PIN_SetPinChannelForPinIndex(i, rel); iChanged++; } } sprintf(tmpA, "e%i", i); if (http_getArg(request->url, tmpA, tmpB, sizeof(tmpB))) { int rel; int prevRel; iChangedRequested++; rel = atoi(tmpB); prevRel = PIN_GetPinChannel2ForPinIndex(i); if (prevRel != rel) { PIN_SetPinChannel2ForPinIndex(i, rel); iChanged++; } } } if (iChangedRequested > 0) { // Anecdotally, if pins are configured badly, the // second-timer breaks. To reconfigure, force // saving the configuration instead of waiting. //CFG_Save_SetupTimer(); CFG_Save_IfThereArePendingChanges(); // Invoke Hass discovery if configuration has changed and not in safe mode. #if ENABLE_HA_DISCOVERY if (!bSafeMode && CFG_HasFlag(OBK_FLAG_AUTOMAIC_HASS_DISCOVERY)) { Main_ScheduleHomeAssistantDiscovery(1); } #endif hprintf255(request, "Pins update - %i reqs, %i changed!

", iChangedRequested, iChanged); } // strcat(outbuf,""); poststr(request, "
"); poststr(request, ""); poststr(request, "
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #if ENABLE_HTTP_FLAGS const char* g_obk_flagNames[] = { "[MQTT] Broadcast led params together (send dimmer and color when dimmer or color changes, topic name: YourDevName/led_basecolor_rgb/get, YourDevName/led_dimmer/get)", "[MQTT] Broadcast led final color (topic name: YourDevName/led_finalcolor_rgb/get)", "[MQTT] Broadcast self state every N (def: 60) seconds (delay configurable by 'mqtt_broadcastInterval' and 'mqtt_broadcastItemsPerSec' commands)", "[LED][Debug] Show raw PWM controller on WWW index instead of new LED RGB/CW/etc picker", "[LED] Force show RGBCW controller (for example, for SM2135 LEDs, or for DGR sender)", "[CMD] Enable TCP console command server (for PuTTY, etc)", "[BTN] Instant touch reaction instead of waiting for release (aka SetOption 13)", "[MQTT] [Debug] Always set Retain flag to all published values", "[LED] Alternate CW light mode (first PWM for warm/cold slider, second for brightness)", "[SM2135] Use separate RGB/CW modes instead of writing all 5 values as RGB", "[MQTT] Broadcast self state on MQTT connect", "[PWM] BK7231 use 600hz instead of 1khz default", "[LED] Remember LED driver state (RGBCW, enable, brightness, temperature) after reboot", "[HTTP] Show actual PIN logic level for unconfigured pins", "[IR] Do MQTT publish (RAW STRING) for incoming IR data", "[IR] Allow 'unknown' protocol", "[MQTT] Broadcast led final color RGBCW (topic name: YourDevName/led_finalcolor_rgbcw/get)", "[LED] Automatically enable Light when changing brightness, color or temperature on WWW panel", "[LED] Smooth transitions for LED (EXPERIMENTAL)", "[MQTT] Always publish channels used by TuyaMCU", "[LED] Force RGB mode (3 PWMs for LEDs) and ignore further PWMs if they are set", "[MQTT] Retain power channels (Relay channels, etc)", "[IR] Do MQTT publish (Tasmota JSON format) for incoming IR data", "[LED] Automatically enable Light on any change of brightness, color or temperature", "[LED] Emulate Cool White with RGB in device with four PWMs - Red is 0, Green 1, Blue 2, and Warm is 4", "[POWER] Allow negative current/power for power measurement (all chips, BL0937, BL0942, etc)", // On BL602, if marked, uses /dev/ttyS1, otherwise S0 // On Beken, if marked, uses UART2, otherwise UART1 "[UART] Use alternate UART for BL0942, CSE, TuyaMCU, etc", "[HASS] Invoke HomeAssistant discovery on change to ip address, configuration", "[LED] Setting RGB white (FFFFFF) enables temperature mode", "[NETIF] Use short device name as a hostname instead of a long name", "[MQTT] Enable Tasmota TELE etc publishes (for ioBroker etc)", "[UART] Enable UART command line", "[LED] Use old linear brightness mode, ignore gamma ramp", "[MQTT] Apply channel type multiplier on (if any) on channel value before publishing it", "[MQTT] In HA discovery, add relays as lights", "[HASS] Deactivate avty_t flag when publishing to HASS (permit to keep value). You must restart HASS discovery for change to take effect.", "[DRV] Deactivate Autostart of all drivers", "[WiFi] Quick connect to WiFi on reboot (TODO: check if it works for you and report on github)", "[Power] Set power and current to zero if all relays are open", "[MQTT] [Debug] Publish all channels (don't enable it, it will be publish all 64 possible channels on connect)", "[MQTT] Use kWh unit for energy consumption (total, last hour, today) instead of Wh", "[BTN] Ignore all button events (aka child lock)", "[DoorSensor] Invert state", "[TuyaMCU] Use queue", "[HTTP] Disable authentication in safe mode (not recommended)", "[MQTT Discovery] Don't merge toggles and dimmers into lights", "[TuyaMCU] Store raw data", "[TuyaMCU] Store ALL data", "[PWR] Invert AC dir", "[HTTP] Hide ON/OFF for relays (only red/green buttons)", "[MQTT] Never add GET suffix", "[WiFi] (RTL/BK/BL602) Enhanced fast connect by saving AP data to flash (preferable with Flag 37 & static ip). Quick reset 3 times to connect normally", "error", "error", "error", "error", }; void uint64_to_str(uint64_t num, char* str) { char temp[21]; // uint64_t 20 numbers + \0 int i = 0; if (num == 0) { temp[i++] = '0'; } else { while (num > 0) { temp[i++] = '0' + (num % 10); num /= 10; } } temp[i] = '\0'; int j; for (j = 0; j < i; j++) { str[j] = temp[i - j - 1]; } str[j] = '\0'; } int http_fn_cfg_generic(http_request_t* request) { int i; char tmpA[64]; char tmpB[64]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Generic config"); if (http_getArg(request->url, "boot_ok_delay", tmpA, sizeof(tmpA))) { i = atoi(tmpA); if (i <= 0) { poststr(request, "
Boot ok delay must be at least 1 second
"); i = 1; } hprintf255(request, "
Setting boot OK delay to %i
", i); CFG_SetBootOkSeconds(i); } if (http_getArg(request->url, "setFlags", tmpA, sizeof(tmpA))) { for (i = 0; i < OBK_TOTAL_FLAGS; i++) { int ni; sprintf(tmpB, "flag%i", i); if (http_getArg(request->url, tmpB, tmpA, sizeof(tmpA))) { ni = atoi(tmpA); } else { ni = 0; } //hprintf255(request, "
Setting flag %i to %i
", i, ni); CFG_SetFlag(i, ni); } } CFG_Save_IfThereArePendingChanges(); // 32 bit type //hprintf255(request, "

Flags (Current value=%i)

", CFG_GetFlags()); // 64 bit - TODO fixme //hprintf255(request, "

Flags (Current value=%llu)

", CFG_GetFlags64()); char buf[21]; uint64_to_str(CFG_GetFlags64(), buf); hprintf255(request, "

Flags (Current value=%s)

", buf); poststr(request, "
"); for (i = 0; i < OBK_TOTAL_FLAGS; i++) { const char* flagName = g_obk_flagNames[i]; /*
*/ hprintf255(request, "
", i, i, (CFG_HasFlag(i) ? " checked" : "")); //this is less that 128 char hprintf255(request, "
"); } poststr(request, ""); poststr(request, SUBMIT_AND_END_FORM); add_label_numeric_field(request, "Uptime in seconds required to mark boot as OK", "boot_ok_delay", CFG_GetBootOkSeconds(), ""); poststr(request, "
"); poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif #if ENABLE_HTTP_STARTUP int http_fn_cfg_startup(http_request_t* request) { int channelIndex; int newValue; int i; char tmpA[128]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Config startup"); poststr_h4(request, "Here you can set pin start values"); poststr(request, "
  • For relays, use 1 or 0
  • "); poststr(request, "
  • To 'remember last power state', use -1 as a special value
  • "); poststr(request, "
  • For dimmers, range is 0 to 100
  • "); poststr(request, "
  • For custom values, you can set any numeric value
  • "); poststr(request, "
  • Remember that you can also use short startup command to run commands like led_baseColor #FF0000 and led_enableAll 1 etc
  • "); hprintf255(request, "
  • To remember last state of LED driver, set "); hprintf255(request, "Flag 12 - %s", g_obk_flagNames[12]); poststr(request, "
"); if (http_getArg(request->url, "idx", tmpA, sizeof(tmpA))) { channelIndex = atoi(tmpA); if (http_getArg(request->url, "value", tmpA, sizeof(tmpA))) { newValue = atoi(tmpA); CFG_SetChannelStartupValue(channelIndex, newValue); // also save current value if marked as saved Channel_SaveInFlashIfNeeded(channelIndex); hprintf255(request, "
Setting channel %i start value to %i
", channelIndex, newValue); CFG_Save_IfThereArePendingChanges(); } } poststr_h4(request, "New start values"); for (i = 0; i < CHANNEL_MAX; i++) { if (CHANNEL_IsInUse(i)) { int startValue = CFG_GetChannelStartupValue(i); poststr(request, "
"); hprintf255(request, "", i); sprintf(tmpA, "Channel %i", i); add_label_numeric_field(request, tmpA, "value", startValue, ""); poststr(request, "

"); } } poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif #if ENABLE_HTTP_DGR int http_fn_cfg_dgr(http_request_t* request) { char tmpA[128]; bool bForceSet; http_setup(request, httpMimeTypeHTML); http_html_start(request, "Device groups"); hprintf255(request, "
Here you can configure Tasmota Device Groups
"); if (http_getArg(request->url, "bSet", tmpA, sizeof(tmpA))) { bForceSet = true; } else { bForceSet = false; } if (http_getArg(request->url, "name", tmpA, sizeof(tmpA)) || bForceSet) { int newSendFlags; int newRecvFlags; newSendFlags = 0; newRecvFlags = 0; if (http_getArgInteger(request->url, "s_pwr")) newSendFlags |= DGR_SHARE_POWER; if (http_getArgInteger(request->url, "r_pwr")) newRecvFlags |= DGR_SHARE_POWER; if (http_getArgInteger(request->url, "s_lbr")) newSendFlags |= DGR_SHARE_LIGHT_BRI; if (http_getArgInteger(request->url, "r_lbr")) newRecvFlags |= DGR_SHARE_LIGHT_BRI; if (http_getArgInteger(request->url, "s_lcl")) newSendFlags |= DGR_SHARE_LIGHT_COLOR; if (http_getArgInteger(request->url, "r_lcl")) newRecvFlags |= DGR_SHARE_LIGHT_COLOR; CFG_DeviceGroups_SetName(tmpA); CFG_DeviceGroups_SetSendFlags(newSendFlags); CFG_DeviceGroups_SetRecvFlags(newRecvFlags); if (tmpA[0] != 0) { #ifndef OBK_DISABLE_ALL_DRIVERS DRV_StartDriver("DGR"); #endif } CFG_Save_IfThereArePendingChanges(); } { int newSendFlags; int newRecvFlags; const char* groupName = CFG_DeviceGroups_GetName(); newSendFlags = CFG_DeviceGroups_GetSendFlags(); newRecvFlags = CFG_DeviceGroups_GetRecvFlags(); add_label_text_field(request, "Group name", "name", groupName, "
"); poststr(request, "
"); poststr(request, " "); poststr(request, ""); poststr(request, " "); poststr(request, ""); poststr(request, " "); poststr(request, ""); poststr(request, "
NameTasmota CodeReceiveSend
Power1
Light Brightness2
Light Color16
"); poststr(request, SUBMIT_AND_END_FORM); } poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } #endif void OTA_RequestDownloadFromHTTP(const char* s) { #if PLATFORM_BEKEN otarequest(s); #elif PLATFORM_ECR6600 extern int http_client_download_file(const char* url); extern int ota_done(bool reset); delay_ms(100); int ret = http_client_download_file(s); if(ret != -1) ota_done(1); else ota_done(0); #elif PLATFORM_W600 || PLATFORM_W800 t_http_fwup(s); #elif PLATFORM_XRADIO uint32_t* verify_value; ota_verify_t verify_type; ota_verify_data_t verify_data; if(ota_get_image(OTA_PROTOCOL_HTTP, s) != OTA_STATUS_OK) { addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "OTA http get image failed"); return; } if(ota_get_verify_data(&verify_data) != OTA_STATUS_OK) { verify_type = OTA_VERIFY_NONE; verify_value = NULL; } else { verify_type = verify_data.ov_type; verify_value = (uint32_t*)(verify_data.ov_data); } if(ota_verify_image(verify_type, verify_value) != OTA_STATUS_OK) { addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "OTA http verify image failed"); return; } ota_reboot(); #elif PLATFORM_REALTEK_NEW ota_context* ctx = NULL; ctx = (ota_context*)malloc(sizeof(ota_context)); if(ctx == NULL) goto exit; memset(ctx, 0, sizeof(ota_context)); char url[256] = { 0 }; char resource[256] = { 0 }; uint16_t port; parser_url(s, &url, &port, &resource, 256); int ret = ota_update_init(ctx, &url, port, &resource, OTA_HTTP); if(ret != 0) { addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "ota_update_init failed"); goto exit; } ret = ota_update_start(ctx); if(!ret) { addLogAdv(LOG_INFO, LOG_FEATURE_HTTP, "OTA finished"); sys_clear_ota_signature(); delay_ms(50); sys_reset(); } exit: ota_update_deinit(ctx); addLogAdv(LOG_ERROR, LOG_FEATURE_HTTP, "OTA failed"); if(ctx) free(ctx); #endif } int http_fn_ota_exec(http_request_t* request) { char tmpA[128]; //char tmpB[64]; http_setup(request, httpMimeTypeHTML); http_html_start(request, "OTA request"); if (http_getArg(request->url, "host", tmpA, sizeof(tmpA))) { hprintf255(request, "

OTA requested for %s!

", tmpA); addLogAdv(LOG_INFO, LOG_FEATURE_HTTP, "http_fn_ota_exec: will try to do OTA for %s \r\n", tmpA); OTA_RequestDownloadFromHTTP(tmpA); } poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } int http_fn_ota(http_request_t* request) { http_setup(request, httpMimeTypeHTML); http_html_start(request, "OTA system"); #ifndef OBK_OTA_EXTENSION poststr(request, "

Sorry, OTA update not implemented for " DEVICENAME_PREFIX_FULL "

"); #else poststr(request, "

It's recommended to use the OTA option in the Web Application, where you can easily drag and drop files.

If you have an HTTP server providing the OTA file, you may enter the URL below. " #if PLATFORM_BEKEN " On Beken platforms, the .rbl file is used for OTA updates." #endif "

"); add_label_text_field(request, "URL for ota firmware file", "host", "", ""); poststr(request, "
\ \
"); const char htmlOTA[] = ""; poststr(request, "


Expert feature: Upload firmware OTA file.
If unsure, please use Web App!


"); poststr(request, ""); poststr(request, ""); poststr(request, htmlOTA); #endif poststr(request, htmlFooterReturnToCfgOrMainPage); http_html_end(request); poststr(request, NULL); return 0; } int http_fn_other(http_request_t* request) { http_setup(request, httpMimeTypeHTML); #if ENABLE_OBK_BERRY if (CMD_Berry_RunEventHandlers_StrPtr(CMD_EVENT_ON_HTTP, request->url, request)) { return 0; } #endif http_html_start(request, "Not found"); poststr(request, "Not found.
"); poststr(request, htmlFooterReturnToMainPage); http_html_end(request); poststr(request, NULL); return 0; }