diff --git a/openBeken_win32_mvsc2017.vcxproj b/openBeken_win32_mvsc2017.vcxproj index 9d34c180b..f88751c5a 100644 --- a/openBeken_win32_mvsc2017.vcxproj +++ b/openBeken_win32_mvsc2017.vcxproj @@ -83,7 +83,7 @@ $(SolutionDir)$(Configuration)\ $(Configuration)\ - openBeken_win32 + openBeken_win32 $(SolutionDir)$(Configuration)\ @@ -92,7 +92,7 @@ $(SolutionDir)$(Configuration)\ $(Configuration)\ - openBeken_win32 + openBeken_win32 $(SolutionDir)$(Configuration)\ @@ -450,6 +450,7 @@ true + true true diff --git a/openBeken_win32_mvsc2017.vcxproj.filters b/openBeken_win32_mvsc2017.vcxproj.filters index 58a093ea1..d51e36d3d 100644 --- a/openBeken_win32_mvsc2017.vcxproj.filters +++ b/openBeken_win32_mvsc2017.vcxproj.filters @@ -477,6 +477,9 @@ SelfTest + + SelfTest + diff --git a/src/httpserver/http_fns.c b/src/httpserver/http_fns.c index 35f441dd9..67a46cc43 100644 --- a/src/httpserver/http_fns.c +++ b/src/httpserver/http_fns.c @@ -1944,536 +1944,7 @@ int http_fn_ha_cfg(http_request_t* request) { return 0; } -// https://tasmota.github.io/docs/Commands/#with-mqtt -/* -http:///cm?cmnd=Power%20TOGGLE -http:///cm?cmnd=Power%20On -http:///cm?cmnd=Power%20off -http:///cm?user=admin&password=joker&cmnd=Power%20Toggle -*/ -// https://www.elektroda.com/rtvforum/viewtopic.php?p=19330027#19330027 -// Web browser sends: GET /cm?cmnd=POWER1 -// System responds with state -int http_tasmota_json_power(http_request_t* request) { - int numRelays; - int numPWMs; - int i; - int lastRelayState; - bool bRelayIndexingStartsWithZero; - int relayIndexingOffset; - int temperature; - int dimmer; - bRelayIndexingStartsWithZero = CHANNEL_HasChannelPinWithRoleOrRole(0, IOR_Relay, IOR_Relay_n); - if (bRelayIndexingStartsWithZero) { - relayIndexingOffset = 0; - } - else { - relayIndexingOffset = 1; - } - - // try to return status - numPWMs = PIN_CountPinsWithRoleOrRole(IOR_PWM, IOR_PWM_n); - numRelays = 0; - - // LED driver (if has PWMs) - if (LED_IsLEDRunning()) { - dimmer = LED_GetDimmer(); - hprintf255(request, "\"Dimmer\":%i,", dimmer); - hprintf255(request, "\"Fade\":\"OFF\","); - hprintf255(request, "\"Speed\":1,"); - hprintf255(request, "\"LedTable\":\"ON\","); - if (LED_IsLedDriverChipRunning() || numPWMs >= 3) { - /* - { - POWER: "OFF", - Dimmer: 100, - Color: "255,0,157", - HSBColor: "323,100,100", - Channel: [ - 100, - 0, - 62 - ] - }*/ - // Eg: Color: "255,0,157", - byte rgbcw[5]; - int hsv[3]; - byte channels[5]; - - LED_GetFinalRGBCW(rgbcw); - LED_GetFinalHSV(hsv); - LED_GetFinalChannels100(channels); - - // it looks like they include C and W in color - if (LED_IsLedDriverChipRunning() || numPWMs == 5) { - hprintf255(request, "\"Color\":\"%i,%i,%i,%i,%i\",", - (int)rgbcw[0], (int)rgbcw[1], (int)rgbcw[2], (int)rgbcw[3], (int)rgbcw[4]); - } - else { - hprintf255(request, "\"Color\":\"%i,%i,%i\",", (int)rgbcw[0], (int)rgbcw[1], (int)rgbcw[2]); - } - hprintf255(request, "\"HSBColor\":\"%i,%i,%i\",", hsv[0], hsv[1], hsv[2]); - hprintf255(request, "\"Channel\":[%i,%i,%i],", (int)channels[0], (int)channels[1], (int)channels[2]); - - } - if (LED_IsLedDriverChipRunning() || numPWMs == 5 || numPWMs == 2) { - // 154 to 500 range - temperature = LED_GetTemperature(); - // Temperature - hprintf255(request, "\"CT\":%i,", temperature); - } - if (LED_GetEnableAll() == 0) { - poststr(request, "\"POWER\":\"OFF\""); - } - else { - poststr(request, "\"POWER\":\"ON\""); - } - } - else { - // relays driver - for (i = 0; i < CHANNEL_MAX; i++) { - if (h_isChannelRelay(i) || CHANNEL_GetType(i) == ChType_Toggle) { - numRelays++; - lastRelayState = CHANNEL_Get(i); - } - } - if (numRelays == 1) { - if (lastRelayState) { - poststr(request, "\"POWER\":\"ON\""); - } - else { - poststr(request, "\"POWER\":\"OFF\""); - } - } - else { - int c_posted = 0; - for (i = 0; i < CHANNEL_MAX; i++) { - if (h_isChannelRelay(i) || CHANNEL_GetType(i) == ChType_Toggle) { - int indexStartingFrom1; - - if (bRelayIndexingStartsWithZero) { - indexStartingFrom1 = i + 1; - } - else { - indexStartingFrom1 = i; - } - lastRelayState = CHANNEL_Get(i); - if (c_posted) { - hprintf255(request, ","); - } - if (lastRelayState) { - hprintf255(request, "\"POWER%i\":\"ON\"", indexStartingFrom1); - } - else { - hprintf255(request, "\"POWER%i\":\"OFF\"", indexStartingFrom1); - } - c_posted++; - } - } - } - - } - return 0; -} -/* -{"StatusSNS":{"Time":"2022-07-30T10:11:26","ENERGY":{"TotalStartTime":"2022-05-12T10:56:31","Total":0.003,"Yesterday":0.003,"Today":0.000,"Power": 0,"ApparentPower": 0,"ReactivePower": 0,"Factor":0.00,"Voltage":236,"Current":0.000}}} -*/ - - -int http_tasmota_json_ENERGY(http_request_t* request) { - float power, factor, voltage, current; - float energy, energy_hour; - - factor = 0; // TODO - voltage = DRV_GetReading(OBK_VOLTAGE); - current = DRV_GetReading(OBK_CURRENT); - power = DRV_GetReading(OBK_POWER); - energy = DRV_GetReading(OBK_CONSUMPTION_TOTAL); - energy_hour = DRV_GetReading(OBK_CONSUMPTION_LAST_HOUR); - - // following check will clear NaN values - if (OBK_IS_NAN(energy)) { - energy = 0; - } - if (OBK_IS_NAN(energy_hour)) { - energy_hour = 0; - } - hprintf255(request, "{"); - hprintf255(request, "\"Power\": %f,", power); - hprintf255(request, "\"ApparentPower\": 0,\"ReactivePower\": 0,\"Factor\":%f,", factor); - hprintf255(request, "\"Voltage\":%f,", voltage); - hprintf255(request, "\"Current\":%f,", current); - hprintf255(request, "\"ConsumptionTotal\":%f,", energy); - hprintf255(request, "\"ConsumptionLastHour\":%f", energy_hour); - // close ENERGY block - hprintf255(request, "}"); - return 0; -} - -// Topic: tele/tasmota_48E7F3/SENSOR at 3:06 AM: -// Sample: -/* -{ - "Time": "2022-12-30T03:06:36", - "ENERGY": { - "TotalStartTime": "2022-05-12T10:56:31", - "Total": 0.007, - "Yesterday": 0, - "Today": 0, - "Period": 0, - "Power": 0, - "ApparentPower": 0, - "ReactivePower": 0, - "Factor": 0, - "Voltage": 241, - "Current": 0 - } -} -*/ -int http_tasmota_json_status_SNS(http_request_t* request) { - char buff[20]; - - hprintf255(request, "\"StatusSNS\":{"); - - time_t localTime = (time_t)NTP_GetCurrentTime(); - strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localTime)); - hprintf255(request, "\"Time\":\"%s\"", buff); - -#ifndef OBK_DISABLE_ALL_DRIVERS - if (DRV_IsMeasuringPower()) { - - // begin ENERGY block - hprintf255(request, ","); - hprintf255(request, "\"ENERGY\":"); - http_tasmota_json_ENERGY(request); - } -#endif - - hprintf255(request, "}"); - - return 0; -} - -#ifdef PLATFORM_XR809 -//XR809 does not support drivers but its build script compiles many drivers including ntp. - -#else -#ifndef ENABLE_BASIC_DRIVERS -unsigned int NTP_GetCurrentTime() { - return 0; -} -unsigned int NTP_GetCurrentTimeWithoutOffset() { - return 0; -} -#endif -#endif - -// Topic: tele/tasmota_48E7F3/STATE -// Sample: -/* -{ - "Time": "2022-12-30T03:06:36", - "Uptime": "0T06:16:14", - "UptimeSec": 22574, - "Heap": 26, - "SleepMode": "Dynamic", - "Sleep": 50, - "LoadAvg": 19, - "MqttCount": 1, - "POWER": "ON", - "Wifi": { - "AP": 1, - "SSId": "ASUS_25G_WIFI", - "BSSId": "32:21:BA:10:F6:6D", - "Channel": 3, - "Mode": "11n", - "RSSI": 62, - "Signal": -69, - "LinkCount": 1, - "Downtime": "0T00:00:04" - } -} -*/ -int http_tasmota_json_status_STS(http_request_t* request) { - char buff[20]; - time_t localTime = (time_t)NTP_GetCurrentTime(); - - hprintf255(request, "{"); - strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localTime)); - hprintf255(request, "\"Time\":\"%s\",", buff); - hprintf255(request, "\"Uptime\":\"30T02:59:30\","); - hprintf255(request, "\"UptimeSec\":%i,", Time_getUpTimeSeconds()); - hprintf255(request, "\"Heap\":25,"); - hprintf255(request, "\"SleepMode\":\"Dynamic\","); - hprintf255(request, "\"Sleep\":10,"); - hprintf255(request, "\"LoadAvg\":99,"); - hprintf255(request, "\"MqttCount\":23,"); - - http_tasmota_json_power(request); - hprintf255(request, ","); - hprintf255(request, "\"Wifi\":{"); // open WiFi - hprintf255(request, "\"AP\":1,"); - hprintf255(request, "\"SSId\":\"%s\",", CFG_GetWiFiSSID()); - hprintf255(request, "\"BSSId\":\"30:B5:C2:5D:70:72\","); - hprintf255(request, "\"Channel\":11,"); - hprintf255(request, "\"Mode\":\"11n\","); - hprintf255(request, "\"RSSI\":78,"); - hprintf255(request, "\"Signal\":%i,", HAL_GetWifiStrength()); - hprintf255(request, "\"LinkCount\":21,"); - hprintf255(request, "\"Downtime\":\"0T06:13:34\""); - hprintf255(request, "}"); // close WiFi - hprintf255(request, "}"); - return 0; -} -/* -{"Status":{"Module":0,"DeviceName":"Tasmota","FriendlyName":["Tasmota"],"Topic":"tasmota_48E7F3","ButtonTopic":"0","Power":1,"PowerOnState":3,"LedState":1,"LedMask":"FFFF","SaveData":1,"SaveState":1,"SwitchTopic":"0","SwitchMode":[0,0,0,0,0,0,0,0],"ButtonRetain":0,"SwitchRetain":0,"SensorRetain":0,"PowerRetain":0,"InfoRetain":0,"StateRetain":0}} -*/ -int http_tasmota_json_status_generic(http_request_t* request) { - const char* deviceName; - const char* friendlyName; - const char* clientId; - int powerCode; - int relayCount, pwmCount, dInputCount, i; - bool bRelayIndexingStartsWithZero; - char buff[20]; - - deviceName = CFG_GetShortDeviceName(); - friendlyName = CFG_GetDeviceName(); - clientId = CFG_GetMQTTClientId(); - - //deviceName = "Tasmota"; - //friendlyName - "Tasmota"; - -#if 0 - const char* dbg = "{\"Status\":{\"Module\":0,\"DeviceName\":\"Tasmota\",\"FriendlyName\":[\"Tasmota\"],\"Topic\":\"tasmota_D79E2C\",\"ButtonTopic\":\"0\",\"Power\":1,\"PowerOnState\":3,\"LedState\":1,\"LedMask\":\"FFFF\",\"SaveData\":1,\"SaveState\":1,\"SwitchTopic\":\"0\",\"SwitchMode\":[0,0,0,0,0,0,0,0],\"ButtonRetain\":0,\"SwitchRetain\":0,\"SensorRetain\":0,\"PowerRetain\":0,\"InfoRetain\":0,\"StateRetain\":0},\"StatusPRM\":{\"Baudrate\":115200,\"SerialConfig\":\"8N1\",\"GroupTopic\":\"tasmotas\",\"OtaUrl\":\"http://ota.tasmota.com/tasmota/release/tasmota.bin.gz\",\"RestartReason\":\"Hardware Watchdog\",\"Uptime\":\"30T03:43:17\",\"StartupUTC\":\"2022-10-10T16:09:41\",\"Sleep\":50,\"CfgHolder\":4617,\"BootCount\":22,\"BCResetTime\":\"2022-01-27T16:10:56\",\"SaveCount\":1235,\"SaveAddress\":\"F9000\"},\"StatusFWR\":{\"Version\":\"10.1.0(tasmota)\",\"BuildDateTime\":\"2021-12-08T14:47:33\",\"Boot\":7,\"Core\":\"2_7_4_9\",\"SDK\":\"2.2.2-dev(38a443e)\",\"CpuFrequency\":80,\"Hardware\":\"ESP8266EX\",\"CR\":\"465/699\"},\"StatusLOG\":{\"SerialLog\":2,\"WebLog\":2,\"MqttLog\":0,\"SysLog\":0,\"LogHost\":\"\",\"LogPort\":514,\"SSId\":[\"DLINK_FastNet\",\"\"],\"TelePeriod\":300,\"Resolution\":\"558180C0\",\"SetOption\":[\"000A8009\",\"2805C80001000600003C5A0A000000000000\",\"00000280\",\"00006008\",\"00004000\"]},\"StatusMEM\":{\"ProgramSize\":616,\"Free\":384,\"Heap\":25,\"ProgramFlashSize\":1024,\"FlashSize\":2048,\"FlashChipId\":\"1540A1\",\"FlashFrequency\":40,\"FlashMode\":3,\"Features\":[\"00000809\",\"8FDAC787\",\"04368001\",\"000000CF\",\"010013C0\",\"C000F981\",\"00004004\",\"00001000\",\"00000020\"],\"Drivers\":\"1,2,3,4,5,6,7,8,9,10,12,16,18,19,20,21,22,24,26,27,29,30,35,37,45\",\"Sensors\":\"1,2,3,4,5,6\"},\"StatusNET\":{\"Hostname\":\"tasmota-D79E2C-7724\",\"IPAddress\":\"192.168.0.104\",\"Gateway\":\"192.168.0.1\",\"Subnetmask\":\"255.255.255.0\",\"DNSServer1\":\"192.168.0.1\",\"DNSServer2\":\"0.0.0.0\",\"Mac\":\"10:52:1C:D7:9E:2C\",\"Webserver\":2,\"HTTP_API\":1,\"WifiConfig\":4,\"WifiPower\":17.0},\"StatusMQT\":{\"MqttHost\":\"192.168.0.113\",\"MqttPort\":1883,\"MqttClientMask\":\"core-mosquitto\",\"MqttClient\":\"core-mosquitto\",\"MqttUser\":\"homeassistant\",\"MqttCount\":23,\"MAX_PACKET_SIZE\":1200,\"KEEPALIVE\":30,\"SOCKET_TIMEOUT\":4},\"StatusTIM\":{\"UTC\":\"2022-11-09T19:52:58\",\"Local\":\"2022-11-09T20:52:58\",\"StartDST\":\"2022-03-27T02:00:00\",\"EndDST\":\"2022-10-30T03:00:00\",\"Timezone\":\"+01:00\",\"Sunrise\":\"07:50\",\"Sunset\":\"17:17\"},\"StatusSNS\":{\"Time\":\"2022-11-09T20:52:58\"},\"StatusSTS\":{\"Time\":\"2022-11-09T20:52:58\",\"Uptime\":\"30T03:43:17\",\"UptimeSec\":2605397,\"Heap\":25,\"SleepMode\":\"Dynamic\",\"Sleep\":10,\"LoadAvg\":99,\"MqttCount\":23,\"POWER\":\"ON\",\"Dimmer\":99,\"Fade\":\"OFF\",\"Speed\":1,\"LedTable\":\"ON\",\"Wifi\":{\"AP\":1,\"SSId\":\"DLINK_FastNet\",\"BSSId\":\"30:B5:C2:5D:70:72\",\"Channel\":11,\"Mode\":\"11n\",\"RSSI\":80,\"Signal\":-60,\"LinkCount\":21,\"Downtime\":\"0T06:13:34\"}}}"; - poststr(request, dbg); - return; - -#endif - - bRelayIndexingStartsWithZero = CHANNEL_HasChannelPinWithRoleOrRole(0, IOR_Relay, IOR_Relay_n); - - get_Relay_PWM_Count(&relayCount, &pwmCount, &dInputCount); - - if (LED_IsLEDRunning()) { - powerCode = LED_GetEnableAll(); - } - else { - powerCode = 0; - for (i = 0; i < CHANNEL_MAX; i++) { - bool bRelay; - int useIdx; - int iValue; - if (bRelayIndexingStartsWithZero) { - useIdx = i; - } - else { - useIdx = i + 1; - } - bRelay = CHANNEL_HasChannelPinWithRoleOrRole(useIdx, IOR_Relay, IOR_Relay_n); - if (bRelay) { - iValue = CHANNEL_Get(useIdx); - if (iValue) - BIT_SET(powerCode, i); - else - BIT_CLEAR(powerCode, i); - } - } - } - - hprintf255(request, "{"); - // Status section - hprintf255(request, "\"Status\":{\"Module\":0,\"DeviceName\":\"%s\"", deviceName); - hprintf255(request, ",\"FriendlyName\":["); - if (relayCount == 0) { - hprintf255(request, "\"%s\"", deviceName); - } - else { - int c_printed = 0; - for (i = 0; i < CHANNEL_MAX; i++) { - bool bRelay; - bRelay = CHANNEL_HasChannelPinWithRoleOrRole(i, IOR_Relay, IOR_Relay_n); - if (bRelay) { - int useIdx; - if (bRelayIndexingStartsWithZero) { - useIdx = i + 1; - } - else { - useIdx = i; - } - if (c_printed) { - hprintf255(request, ","); - } - hprintf255(request, "\"%s_%i\"", deviceName, useIdx); - c_printed++; - } - } - } - hprintf255(request, "]"); - hprintf255(request, ",\"Topic\":\"%s\",\"ButtonTopic\":\"0\"", clientId); - hprintf255(request, ",\"Power\":%i,\"PowerOnState\":3,\"LedState\":1", powerCode); - hprintf255(request, ",\"LedMask\":\"FFFF\",\"SaveData\":1,\"SaveState\":1"); - hprintf255(request, ",\"SwitchTopic\":\"0\",\"SwitchMode\":[0,0,0,0,0,0,0,0]"); - hprintf255(request, ",\"ButtonRetain\":0,\"SwitchRetain\":0,\"SensorRetain\":0"); - hprintf255(request, ",\"PowerRetain\":0,\"InfoRetain\":0,\"StateRetain\":0"); - hprintf255(request, "}"); - - hprintf255(request, ","); - - - hprintf255(request, "\"StatusPRM\":{"); - hprintf255(request, "\"Baudrate\":115200,"); - hprintf255(request, "\"SerialConfig\":\"8N1\","); - hprintf255(request, "\"GroupTopic\":\"tasmotas\","); - hprintf255(request, "\"OtaUrl\":\"http://ota.tasmota.com/tasmota/release/tasmota.bin.gz\","); - hprintf255(request, "\"RestartReason\":\"HardwareWatchdog\","); - hprintf255(request, "\"Uptime\":\"30T02:59:30\","); - hprintf255(request, "\"StartupUTC\":\"2022-10-10T16:09:41\","); - hprintf255(request, "\"Sleep\":50,"); - hprintf255(request, "\"CfgHolder\":4617,"); - hprintf255(request, "\"BootCount\":22,"); - hprintf255(request, "\"BCResetTime\":\"2022-01-27T16:10:56\","); - hprintf255(request, "\"SaveCount\":1235,"); - hprintf255(request, "\"SaveAddress\":\"F9000\""); - hprintf255(request, "}"); - - hprintf255(request, ","); - - hprintf255(request, "\"StatusFWR\":{"); - hprintf255(request, "\"Version\":\"%s\",", DEVICENAME_PREFIX_FULL"_"USER_SW_VER); - hprintf255(request, "\"BuildDateTime\":\"%s\",", __DATE__ " " __TIME__); - hprintf255(request, "\"Boot\":7,"); - hprintf255(request, "\"Core\":\"%s\",", "0.0"); - hprintf255(request, "\"SDK\":\"\",", "obk"); - hprintf255(request, "\"CpuFrequency\":80,"); - hprintf255(request, "\"Hardware\":\"%s\",", PLATFORM_MCU_NAME); - hprintf255(request, "\"CR\":\"465/699\""); - hprintf255(request, "}"); - - hprintf255(request, ","); - - - - hprintf255(request, "\"StatusLOG\":{"); - hprintf255(request, "\"SerialLog\":2,"); - hprintf255(request, "\"WebLog\":2,"); - hprintf255(request, "\"MqttLog\":0,"); - hprintf255(request, "\"SysLog\":0,"); - hprintf255(request, "\"LogHost\":\"\","); - hprintf255(request, "\"LogPort\":514,"); - hprintf255(request, "\"SSId\":["); - hprintf255(request, "\"%s\",", CFG_GetWiFiSSID()); - hprintf255(request, "\"\""); - hprintf255(request, "],"); - hprintf255(request, "\"TelePeriod\":300,"); - hprintf255(request, "\"Resolution\":\"558180C0\","); - hprintf255(request, "\"SetOption\":["); - hprintf255(request, "\"000A8009\","); - hprintf255(request, "\"2805C80001000600003C5A0A000000000000\","); - hprintf255(request, "\"00000280\","); - hprintf255(request, "\"00006008\","); - hprintf255(request, "\"00004000\""); - hprintf255(request, "]"); - hprintf255(request, "}"); - - hprintf255(request, ","); - - - - hprintf255(request, "\"StatusMEM\":{"); - hprintf255(request, "\"ProgramSize\":616,"); - hprintf255(request, "\"Free\":384,"); - hprintf255(request, "\"Heap\":25,"); - hprintf255(request, "\"ProgramFlashSize\":1024,"); - hprintf255(request, "\"FlashSize\":2048,"); - hprintf255(request, "\"FlashChipId\":\"1540A1\","); - hprintf255(request, "\"FlashFrequency\":40,"); - hprintf255(request, "\"FlashMode\":3,"); - hprintf255(request, "\"Features\":["); - hprintf255(request, "\"00000809\","); - hprintf255(request, "\"8FDAC787\","); - hprintf255(request, "\"04368001\","); - hprintf255(request, "\"000000CF\","); - hprintf255(request, "\"010013C0\","); - hprintf255(request, "\"C000F981\","); - hprintf255(request, "\"00004004\","); - hprintf255(request, "\"00001000\","); - hprintf255(request, "\"00000020\""); - hprintf255(request, "],"); - hprintf255(request, "\"Drivers\":\"1,2,3,4,5,6,7,8,9,10,12,16,18,19,20,21,22,24,26,27,29,30,35,37,45\","); - hprintf255(request, "\"Sensors\":\"1,2,3,4,5,6\""); - hprintf255(request, "}"); - - hprintf255(request, ","); - - - hprintf255(request, "\"StatusNET\":{"); - hprintf255(request, "\"Hostname\":\"%s\",", CFG_GetShortDeviceName()); - hprintf255(request, "\"IPAddress\":\"%s\",", HAL_GetMyIPString()); - hprintf255(request, "\"Gateway\":\"192.168.0.1\","); - hprintf255(request, "\"Subnetmask\":\"255.255.255.0\","); - hprintf255(request, "\"DNSServer1\":\"192.168.0.1\","); - hprintf255(request, "\"DNSServer2\":\"0.0.0.0\","); - hprintf255(request, "\"Mac\":\"10:52:1C:D7:9E:2C\","); - hprintf255(request, "\"Webserver\":2,"); - hprintf255(request, "\"HTTP_API\":1,"); - hprintf255(request, "\"WifiConfig\":4,"); - hprintf255(request, "\"WifiPower\":17.0"); - hprintf255(request, "}"); - hprintf255(request, ","); - - - - - hprintf255(request, "\"StatusMQT\":{"); - hprintf255(request, "\"MqttHost\":\"%s\",", CFG_GetMQTTHost()); - hprintf255(request, "\"MqttPort\":%i,", CFG_GetMQTTPort()); - hprintf255(request, "\"MqttClientMask\":\"core-mosquitto\","); - hprintf255(request, "\"MqttClient\":\"%s\",", CFG_GetMQTTClientId()); - hprintf255(request, "\"MqttUser\":\"%s\",", CFG_GetMQTTUserName()); - hprintf255(request, "\"MqttCount\":23,"); - hprintf255(request, "\"MAX_PACKET_SIZE\":1200,"); - hprintf255(request, "\"KEEPALIVE\":30,"); - hprintf255(request, "\"SOCKET_TIMEOUT\":4"); - hprintf255(request, "}"); - - hprintf255(request, ","); - time_t localTime = (time_t)NTP_GetCurrentTime(); - { - time_t localUTC = (time_t)NTP_GetCurrentTimeWithoutOffset(); - - hprintf255(request, "\"StatusTIM\":{"); - strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localUTC)); - hprintf255(request, "\"UTC\":\"%s\",", buff); - strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localTime)); - hprintf255(request, "\"Local\":\"%s\",", buff); - hprintf255(request, "\"StartDST\":\"2022-03-27T02:00:00\","); - hprintf255(request, "\"EndDST\":\"2022-10-30T03:00:00\","); - hprintf255(request, "\"Timezone\":\"+01:00\","); - hprintf255(request, "\"Sunrise\":\"07:50\","); - hprintf255(request, "\"Sunset\":\"17:17\""); - hprintf255(request, "}"); - } - - hprintf255(request, ","); - - - http_tasmota_json_status_SNS(request); - - hprintf255(request, ","); - - hprintf255(request, "\"StatusSTS\":"); - - http_tasmota_json_status_STS(request); - - // end - hprintf255(request, "}"); - - - - return 0; -} int http_fn_cm(http_request_t* request) { char tmpA[128]; char *long_str_alloced = 0; @@ -2489,28 +1960,14 @@ int http_fn_cm(http_request_t* request) { if (long_str_alloced) { http_getArg(request->url, "cmnd", long_str_alloced, commandLen); CMD_ExecuteCommand(long_str_alloced, COMMAND_FLAG_SOURCE_HTTP); + JSON_ProcessCommandReply(long_str_alloced, request, hprintf255, COMMAND_FLAG_SOURCE_HTTP); free(long_str_alloced); } } else { CMD_ExecuteCommand(tmpA, COMMAND_FLAG_SOURCE_HTTP); + JSON_ProcessCommandReply(tmpA, request, hprintf255, COMMAND_FLAG_SOURCE_HTTP); } - - if (!wal_strnicmp(tmpA, "POWER", 5)) { - - poststr(request, "{"); - http_tasmota_json_power(request); - poststr(request, "}"); - } - else if (!wal_strnicmp(tmpA, "STATUS 8", 8) || !wal_strnicmp(tmpA, "STATUS 10", 10)) { - hprintf255(request, "{"); - http_tasmota_json_status_SNS(request); - hprintf255(request, "}"); - } - else { - http_tasmota_json_status_generic(request); - } - } poststr(request, NULL); diff --git a/src/httpserver/json_interface.c b/src/httpserver/json_interface.c new file mode 100644 index 000000000..a03d3dcc2 --- /dev/null +++ b/src/httpserver/json_interface.c @@ -0,0 +1,586 @@ +#include "../new_common.h" +#include "http_fns.h" +#include "../new_pins.h" +#include "../new_cfg.h" +#include "../ota/ota.h" +// Commands register, execution API and cmd tokenizer +#include "../cmnds/cmd_public.h" +#include "../driver/drv_tuyaMCU.h" +#include "../driver/drv_public.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" + + + +// https://tasmota.github.io/docs/Commands/#with-mqtt +/* +http:///cm?cmnd=Power%20TOGGLE +http:///cm?cmnd=Power%20On +http:///cm?cmnd=Power%20off +http:///cm?user=admin&password=joker&cmnd=Power%20Toggle +*/ +// https://www.elektroda.com/rtvforum/viewtopic.php?p=19330027#19330027 +// Web browser sends: GET /cm?cmnd=POWER1 +// System responds with state +static int http_tasmota_json_power(void* request, jsonCb_t printer) { + int numRelays; + int numPWMs; + int i; + int lastRelayState; + bool bRelayIndexingStartsWithZero; + int relayIndexingOffset; + int temperature; + int dimmer; + + bRelayIndexingStartsWithZero = CHANNEL_HasChannelPinWithRoleOrRole(0, IOR_Relay, IOR_Relay_n); + if (bRelayIndexingStartsWithZero) { + relayIndexingOffset = 0; + } + else { + relayIndexingOffset = 1; + } + + // try to return status + numPWMs = PIN_CountPinsWithRoleOrRole(IOR_PWM, IOR_PWM_n); + numRelays = 0; + + // LED driver (if has PWMs) + if (LED_IsLEDRunning()) { + dimmer = LED_GetDimmer(); + printer(request, "\"Dimmer\":%i,", dimmer); + printer(request, "\"Fade\":\"OFF\","); + printer(request, "\"Speed\":1,"); + printer(request, "\"LedTable\":\"ON\","); + if (LED_IsLedDriverChipRunning() || numPWMs >= 3) { + /* + { + POWER: "OFF", + Dimmer: 100, + Color: "255,0,157", + HSBColor: "323,100,100", + Channel: [ + 100, + 0, + 62 + ] + }*/ + // Eg: Color: "255,0,157", + byte rgbcw[5]; + int hsv[3]; + byte channels[5]; + + LED_GetFinalRGBCW(rgbcw); + LED_GetFinalHSV(hsv); + LED_GetFinalChannels100(channels); + + // it looks like they include C and W in color + if (LED_IsLedDriverChipRunning() || numPWMs == 5) { + printer(request, "\"Color\":\"%i,%i,%i,%i,%i\",", + (int)rgbcw[0], (int)rgbcw[1], (int)rgbcw[2], (int)rgbcw[3], (int)rgbcw[4]); + } + else { + printer(request, "\"Color\":\"%i,%i,%i\",", (int)rgbcw[0], (int)rgbcw[1], (int)rgbcw[2]); + } + printer(request, "\"HSBColor\":\"%i,%i,%i\",", hsv[0], hsv[1], hsv[2]); + printer(request, "\"Channel\":[%i,%i,%i],", (int)channels[0], (int)channels[1], (int)channels[2]); + + } + if (LED_IsLedDriverChipRunning() || numPWMs == 5 || numPWMs == 2) { + // 154 to 500 range + temperature = LED_GetTemperature(); + // Temperature + printer(request, "\"CT\":%i,", temperature); + } + if (LED_GetEnableAll() == 0) { + printer(request, "\"POWER\":\"OFF\""); + } + else { + printer(request, "\"POWER\":\"ON\""); + } + } + else { + // relays driver + for (i = 0; i < CHANNEL_MAX; i++) { + if (h_isChannelRelay(i) || CHANNEL_GetType(i) == ChType_Toggle) { + numRelays++; + lastRelayState = CHANNEL_Get(i); + } + } + if (numRelays == 1) { + if (lastRelayState) { + printer(request, "\"POWER\":\"ON\""); + } + else { + printer(request, "\"POWER\":\"OFF\""); + } + } + else { + int c_posted = 0; + for (i = 0; i < CHANNEL_MAX; i++) { + if (h_isChannelRelay(i) || CHANNEL_GetType(i) == ChType_Toggle) { + int indexStartingFrom1; + + if (bRelayIndexingStartsWithZero) { + indexStartingFrom1 = i + 1; + } + else { + indexStartingFrom1 = i; + } + lastRelayState = CHANNEL_Get(i); + if (c_posted) { + printer(request, ","); + } + if (lastRelayState) { + printer(request, "\"POWER%i\":\"ON\"", indexStartingFrom1); + } + else { + printer(request, "\"POWER%i\":\"OFF\"", indexStartingFrom1); + } + c_posted++; + } + } + } + + } + return 0; +} +/* +{"StatusSNS":{"Time":"2022-07-30T10:11:26","ENERGY":{"TotalStartTime":"2022-05-12T10:56:31","Total":0.003,"Yesterday":0.003,"Today":0.000,"Power": 0,"ApparentPower": 0,"ReactivePower": 0,"Factor":0.00,"Voltage":236,"Current":0.000}}} +*/ + + +static int http_tasmota_json_ENERGY(void* request, jsonCb_t printer) { + float power, factor, voltage, current; + float energy, energy_hour; + + factor = 0; // TODO + voltage = DRV_GetReading(OBK_VOLTAGE); + current = DRV_GetReading(OBK_CURRENT); + power = DRV_GetReading(OBK_POWER); + energy = DRV_GetReading(OBK_CONSUMPTION_TOTAL); + energy_hour = DRV_GetReading(OBK_CONSUMPTION_LAST_HOUR); + + // following check will clear NaN values + if (OBK_IS_NAN(energy)) { + energy = 0; + } + if (OBK_IS_NAN(energy_hour)) { + energy_hour = 0; + } + printer(request, "{"); + printer(request, "\"Power\": %f,", power); + printer(request, "\"ApparentPower\": 0,\"ReactivePower\": 0,\"Factor\":%f,", factor); + printer(request, "\"Voltage\":%f,", voltage); + printer(request, "\"Current\":%f,", current); + printer(request, "\"ConsumptionTotal\":%f,", energy); + printer(request, "\"ConsumptionLastHour\":%f", energy_hour); + // close ENERGY block + printer(request, "}"); + return 0; +} + +// Topic: tele/tasmota_48E7F3/SENSOR at 3:06 AM: +// Sample: +/* +{ + "Time": "2022-12-30T03:06:36", + "ENERGY": { + "TotalStartTime": "2022-05-12T10:56:31", + "Total": 0.007, + "Yesterday": 0, + "Today": 0, + "Period": 0, + "Power": 0, + "ApparentPower": 0, + "ReactivePower": 0, + "Factor": 0, + "Voltage": 241, + "Current": 0 + } +} +*/ +static int http_tasmota_json_status_SNS(void* request, jsonCb_t printer) { + char buff[20]; + + printer(request, "\"StatusSNS\":{"); + + time_t localTime = (time_t)NTP_GetCurrentTime(); + strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localTime)); + printer(request, "\"Time\":\"%s\"", buff); + +#ifndef OBK_DISABLE_ALL_DRIVERS + if (DRV_IsMeasuringPower()) { + + // begin ENERGY block + printer(request, ","); + printer(request, "\"ENERGY\":"); + http_tasmota_json_ENERGY(request, printer); + } +#endif + + printer(request, "}"); + + return 0; +} + +#ifdef PLATFORM_XR809 +//XR809 does not support drivers but its build script compiles many drivers including ntp. + +#else +#ifndef ENABLE_BASIC_DRIVERS +unsigned int NTP_GetCurrentTime() { + return 0; +} +unsigned int NTP_GetCurrentTimeWithoutOffset() { + return 0; +} +#endif +#endif + +// Topic: tele/tasmota_48E7F3/STATE +// Sample: +/* +{ + "Time": "2022-12-30T03:06:36", + "Uptime": "0T06:16:14", + "UptimeSec": 22574, + "Heap": 26, + "SleepMode": "Dynamic", + "Sleep": 50, + "LoadAvg": 19, + "MqttCount": 1, + "POWER": "ON", + "Wifi": { + "AP": 1, + "SSId": "ASUS_25G_WIFI", + "BSSId": "32:21:BA:10:F6:6D", + "Channel": 3, + "Mode": "11n", + "RSSI": 62, + "Signal": -69, + "LinkCount": 1, + "Downtime": "0T00:00:04" + } +} +*/ +static int http_tasmota_json_status_STS(void* request, jsonCb_t printer) { + char buff[20]; + time_t localTime = (time_t)NTP_GetCurrentTime(); + + printer(request, "{"); + strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localTime)); + printer(request, "\"Time\":\"%s\",", buff); + printer(request, "\"Uptime\":\"30T02:59:30\","); + printer(request, "\"UptimeSec\":%i,", Time_getUpTimeSeconds()); + printer(request, "\"Heap\":25,"); + printer(request, "\"SleepMode\":\"Dynamic\","); + printer(request, "\"Sleep\":10,"); + printer(request, "\"LoadAvg\":99,"); + printer(request, "\"MqttCount\":23,"); + + http_tasmota_json_power(request, printer); + printer(request, ","); + printer(request, "\"Wifi\":{"); // open WiFi + printer(request, "\"AP\":1,"); + printer(request, "\"SSId\":\"%s\",", CFG_GetWiFiSSID()); + printer(request, "\"BSSId\":\"30:B5:C2:5D:70:72\","); + printer(request, "\"Channel\":11,"); + printer(request, "\"Mode\":\"11n\","); + printer(request, "\"RSSI\":78,"); + printer(request, "\"Signal\":%i,", HAL_GetWifiStrength()); + printer(request, "\"LinkCount\":21,"); + printer(request, "\"Downtime\":\"0T06:13:34\""); + printer(request, "}"); // close WiFi + printer(request, "}"); + return 0; +} +/* +{"Status":{"Module":0,"DeviceName":"Tasmota","FriendlyName":["Tasmota"],"Topic":"tasmota_48E7F3","ButtonTopic":"0","Power":1,"PowerOnState":3,"LedState":1,"LedMask":"FFFF","SaveData":1,"SaveState":1,"SwitchTopic":"0","SwitchMode":[0,0,0,0,0,0,0,0],"ButtonRetain":0,"SwitchRetain":0,"SensorRetain":0,"PowerRetain":0,"InfoRetain":0,"StateRetain":0}} +*/ +static int http_tasmota_json_status_generic(void* request, jsonCb_t printer) { + const char* deviceName; + const char* friendlyName; + const char* clientId; + int powerCode; + int relayCount, pwmCount, dInputCount, i; + bool bRelayIndexingStartsWithZero; + char buff[20]; + + deviceName = CFG_GetShortDeviceName(); + friendlyName = CFG_GetDeviceName(); + clientId = CFG_GetMQTTClientId(); + + //deviceName = "Tasmota"; + //friendlyName - "Tasmota"; + +#if 0 + const char* dbg = "{\"Status\":{\"Module\":0,\"DeviceName\":\"Tasmota\",\"FriendlyName\":[\"Tasmota\"],\"Topic\":\"tasmota_D79E2C\",\"ButtonTopic\":\"0\",\"Power\":1,\"PowerOnState\":3,\"LedState\":1,\"LedMask\":\"FFFF\",\"SaveData\":1,\"SaveState\":1,\"SwitchTopic\":\"0\",\"SwitchMode\":[0,0,0,0,0,0,0,0],\"ButtonRetain\":0,\"SwitchRetain\":0,\"SensorRetain\":0,\"PowerRetain\":0,\"InfoRetain\":0,\"StateRetain\":0},\"StatusPRM\":{\"Baudrate\":115200,\"SerialConfig\":\"8N1\",\"GroupTopic\":\"tasmotas\",\"OtaUrl\":\"http://ota.tasmota.com/tasmota/release/tasmota.bin.gz\",\"RestartReason\":\"Hardware Watchdog\",\"Uptime\":\"30T03:43:17\",\"StartupUTC\":\"2022-10-10T16:09:41\",\"Sleep\":50,\"CfgHolder\":4617,\"BootCount\":22,\"BCResetTime\":\"2022-01-27T16:10:56\",\"SaveCount\":1235,\"SaveAddress\":\"F9000\"},\"StatusFWR\":{\"Version\":\"10.1.0(tasmota)\",\"BuildDateTime\":\"2021-12-08T14:47:33\",\"Boot\":7,\"Core\":\"2_7_4_9\",\"SDK\":\"2.2.2-dev(38a443e)\",\"CpuFrequency\":80,\"Hardware\":\"ESP8266EX\",\"CR\":\"465/699\"},\"StatusLOG\":{\"SerialLog\":2,\"WebLog\":2,\"MqttLog\":0,\"SysLog\":0,\"LogHost\":\"\",\"LogPort\":514,\"SSId\":[\"DLINK_FastNet\",\"\"],\"TelePeriod\":300,\"Resolution\":\"558180C0\",\"SetOption\":[\"000A8009\",\"2805C80001000600003C5A0A000000000000\",\"00000280\",\"00006008\",\"00004000\"]},\"StatusMEM\":{\"ProgramSize\":616,\"Free\":384,\"Heap\":25,\"ProgramFlashSize\":1024,\"FlashSize\":2048,\"FlashChipId\":\"1540A1\",\"FlashFrequency\":40,\"FlashMode\":3,\"Features\":[\"00000809\",\"8FDAC787\",\"04368001\",\"000000CF\",\"010013C0\",\"C000F981\",\"00004004\",\"00001000\",\"00000020\"],\"Drivers\":\"1,2,3,4,5,6,7,8,9,10,12,16,18,19,20,21,22,24,26,27,29,30,35,37,45\",\"Sensors\":\"1,2,3,4,5,6\"},\"StatusNET\":{\"Hostname\":\"tasmota-D79E2C-7724\",\"IPAddress\":\"192.168.0.104\",\"Gateway\":\"192.168.0.1\",\"Subnetmask\":\"255.255.255.0\",\"DNSServer1\":\"192.168.0.1\",\"DNSServer2\":\"0.0.0.0\",\"Mac\":\"10:52:1C:D7:9E:2C\",\"Webserver\":2,\"HTTP_API\":1,\"WifiConfig\":4,\"WifiPower\":17.0},\"StatusMQT\":{\"MqttHost\":\"192.168.0.113\",\"MqttPort\":1883,\"MqttClientMask\":\"core-mosquitto\",\"MqttClient\":\"core-mosquitto\",\"MqttUser\":\"homeassistant\",\"MqttCount\":23,\"MAX_PACKET_SIZE\":1200,\"KEEPALIVE\":30,\"SOCKET_TIMEOUT\":4},\"StatusTIM\":{\"UTC\":\"2022-11-09T19:52:58\",\"Local\":\"2022-11-09T20:52:58\",\"StartDST\":\"2022-03-27T02:00:00\",\"EndDST\":\"2022-10-30T03:00:00\",\"Timezone\":\"+01:00\",\"Sunrise\":\"07:50\",\"Sunset\":\"17:17\"},\"StatusSNS\":{\"Time\":\"2022-11-09T20:52:58\"},\"StatusSTS\":{\"Time\":\"2022-11-09T20:52:58\",\"Uptime\":\"30T03:43:17\",\"UptimeSec\":2605397,\"Heap\":25,\"SleepMode\":\"Dynamic\",\"Sleep\":10,\"LoadAvg\":99,\"MqttCount\":23,\"POWER\":\"ON\",\"Dimmer\":99,\"Fade\":\"OFF\",\"Speed\":1,\"LedTable\":\"ON\",\"Wifi\":{\"AP\":1,\"SSId\":\"DLINK_FastNet\",\"BSSId\":\"30:B5:C2:5D:70:72\",\"Channel\":11,\"Mode\":\"11n\",\"RSSI\":80,\"Signal\":-60,\"LinkCount\":21,\"Downtime\":\"0T06:13:34\"}}}"; + printer(request, dbg); + return; + +#endif + + bRelayIndexingStartsWithZero = CHANNEL_HasChannelPinWithRoleOrRole(0, IOR_Relay, IOR_Relay_n); + + get_Relay_PWM_Count(&relayCount, &pwmCount, &dInputCount); + + if (LED_IsLEDRunning()) { + powerCode = LED_GetEnableAll(); + } + else { + powerCode = 0; + for (i = 0; i < CHANNEL_MAX; i++) { + bool bRelay; + int useIdx; + int iValue; + if (bRelayIndexingStartsWithZero) { + useIdx = i; + } + else { + useIdx = i + 1; + } + bRelay = CHANNEL_HasChannelPinWithRoleOrRole(useIdx, IOR_Relay, IOR_Relay_n); + if (bRelay) { + iValue = CHANNEL_Get(useIdx); + if (iValue) + BIT_SET(powerCode, i); + else + BIT_CLEAR(powerCode, i); + } + } + } + + printer(request, "{"); + // Status section + printer(request, "\"Status\":{\"Module\":0,\"DeviceName\":\"%s\"", deviceName); + printer(request, ",\"FriendlyName\":["); + if (relayCount == 0) { + printer(request, "\"%s\"", deviceName); + } + else { + int c_printed = 0; + for (i = 0; i < CHANNEL_MAX; i++) { + bool bRelay; + bRelay = CHANNEL_HasChannelPinWithRoleOrRole(i, IOR_Relay, IOR_Relay_n); + if (bRelay) { + int useIdx; + if (bRelayIndexingStartsWithZero) { + useIdx = i + 1; + } + else { + useIdx = i; + } + if (c_printed) { + printer(request, ","); + } + printer(request, "\"%s_%i\"", deviceName, useIdx); + c_printed++; + } + } + } + printer(request, "]"); + printer(request, ",\"Topic\":\"%s\",\"ButtonTopic\":\"0\"", clientId); + printer(request, ",\"Power\":%i,\"PowerOnState\":3,\"LedState\":1", powerCode); + printer(request, ",\"LedMask\":\"FFFF\",\"SaveData\":1,\"SaveState\":1"); + printer(request, ",\"SwitchTopic\":\"0\",\"SwitchMode\":[0,0,0,0,0,0,0,0]"); + printer(request, ",\"ButtonRetain\":0,\"SwitchRetain\":0,\"SensorRetain\":0"); + printer(request, ",\"PowerRetain\":0,\"InfoRetain\":0,\"StateRetain\":0"); + printer(request, "}"); + + printer(request, ","); + + + printer(request, "\"StatusPRM\":{"); + printer(request, "\"Baudrate\":115200,"); + printer(request, "\"SerialConfig\":\"8N1\","); + printer(request, "\"GroupTopic\":\"tasmotas\","); + printer(request, "\"OtaUrl\":\"http://ota.tasmota.com/tasmota/release/tasmota.bin.gz\","); + printer(request, "\"RestartReason\":\"HardwareWatchdog\","); + printer(request, "\"Uptime\":\"30T02:59:30\","); + printer(request, "\"StartupUTC\":\"2022-10-10T16:09:41\","); + printer(request, "\"Sleep\":50,"); + printer(request, "\"CfgHolder\":4617,"); + printer(request, "\"BootCount\":22,"); + printer(request, "\"BCResetTime\":\"2022-01-27T16:10:56\","); + printer(request, "\"SaveCount\":1235,"); + printer(request, "\"SaveAddress\":\"F9000\""); + printer(request, "}"); + + printer(request, ","); + + printer(request, "\"StatusFWR\":{"); + printer(request, "\"Version\":\"%s\",", DEVICENAME_PREFIX_FULL"_"USER_SW_VER); + printer(request, "\"BuildDateTime\":\"%s\",", __DATE__ " " __TIME__); + printer(request, "\"Boot\":7,"); + printer(request, "\"Core\":\"%s\",", "0.0"); + printer(request, "\"SDK\":\"\",", "obk"); + printer(request, "\"CpuFrequency\":80,"); + printer(request, "\"Hardware\":\"%s\",", PLATFORM_MCU_NAME); + printer(request, "\"CR\":\"465/699\""); + printer(request, "}"); + + printer(request, ","); + + + + printer(request, "\"StatusLOG\":{"); + printer(request, "\"SerialLog\":2,"); + printer(request, "\"WebLog\":2,"); + printer(request, "\"MqttLog\":0,"); + printer(request, "\"SysLog\":0,"); + printer(request, "\"LogHost\":\"\","); + printer(request, "\"LogPort\":514,"); + printer(request, "\"SSId\":["); + printer(request, "\"%s\",", CFG_GetWiFiSSID()); + printer(request, "\"\""); + printer(request, "],"); + printer(request, "\"TelePeriod\":300,"); + printer(request, "\"Resolution\":\"558180C0\","); + printer(request, "\"SetOption\":["); + printer(request, "\"000A8009\","); + printer(request, "\"2805C80001000600003C5A0A000000000000\","); + printer(request, "\"00000280\","); + printer(request, "\"00006008\","); + printer(request, "\"00004000\""); + printer(request, "]"); + printer(request, "}"); + + printer(request, ","); + + + + printer(request, "\"StatusMEM\":{"); + printer(request, "\"ProgramSize\":616,"); + printer(request, "\"Free\":384,"); + printer(request, "\"Heap\":25,"); + printer(request, "\"ProgramFlashSize\":1024,"); + printer(request, "\"FlashSize\":2048,"); + printer(request, "\"FlashChipId\":\"1540A1\","); + printer(request, "\"FlashFrequency\":40,"); + printer(request, "\"FlashMode\":3,"); + printer(request, "\"Features\":["); + printer(request, "\"00000809\","); + printer(request, "\"8FDAC787\","); + printer(request, "\"04368001\","); + printer(request, "\"000000CF\","); + printer(request, "\"010013C0\","); + printer(request, "\"C000F981\","); + printer(request, "\"00004004\","); + printer(request, "\"00001000\","); + printer(request, "\"00000020\""); + printer(request, "],"); + printer(request, "\"Drivers\":\"1,2,3,4,5,6,7,8,9,10,12,16,18,19,20,21,22,24,26,27,29,30,35,37,45\","); + printer(request, "\"Sensors\":\"1,2,3,4,5,6\""); + printer(request, "}"); + + printer(request, ","); + + + printer(request, "\"StatusNET\":{"); + printer(request, "\"Hostname\":\"%s\",", CFG_GetShortDeviceName()); + printer(request, "\"IPAddress\":\"%s\",", HAL_GetMyIPString()); + printer(request, "\"Gateway\":\"192.168.0.1\","); + printer(request, "\"Subnetmask\":\"255.255.255.0\","); + printer(request, "\"DNSServer1\":\"192.168.0.1\","); + printer(request, "\"DNSServer2\":\"0.0.0.0\","); + printer(request, "\"Mac\":\"10:52:1C:D7:9E:2C\","); + printer(request, "\"Webserver\":2,"); + printer(request, "\"HTTP_API\":1,"); + printer(request, "\"WifiConfig\":4,"); + printer(request, "\"WifiPower\":17.0"); + printer(request, "}"); + printer(request, ","); + + + + + printer(request, "\"StatusMQT\":{"); + printer(request, "\"MqttHost\":\"%s\",", CFG_GetMQTTHost()); + printer(request, "\"MqttPort\":%i,", CFG_GetMQTTPort()); + printer(request, "\"MqttClientMask\":\"core-mosquitto\","); + printer(request, "\"MqttClient\":\"%s\",", CFG_GetMQTTClientId()); + printer(request, "\"MqttUser\":\"%s\",", CFG_GetMQTTUserName()); + printer(request, "\"MqttCount\":23,"); + printer(request, "\"MAX_PACKET_SIZE\":1200,"); + printer(request, "\"KEEPALIVE\":30,"); + printer(request, "\"SOCKET_TIMEOUT\":4"); + printer(request, "}"); + + printer(request, ","); + time_t localTime = (time_t)NTP_GetCurrentTime(); + { + time_t localUTC = (time_t)NTP_GetCurrentTimeWithoutOffset(); + + printer(request, "\"StatusTIM\":{"); + strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localUTC)); + printer(request, "\"UTC\":\"%s\",", buff); + strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%S", localtime(&localTime)); + printer(request, "\"Local\":\"%s\",", buff); + printer(request, "\"StartDST\":\"2022-03-27T02:00:00\","); + printer(request, "\"EndDST\":\"2022-10-30T03:00:00\","); + printer(request, "\"Timezone\":\"+01:00\","); + printer(request, "\"Sunrise\":\"07:50\","); + printer(request, "\"Sunset\":\"17:17\""); + printer(request, "}"); + } + + printer(request, ","); + + + http_tasmota_json_status_SNS(request, printer); + + printer(request, ","); + + printer(request, "\"StatusSTS\":"); + + http_tasmota_json_status_STS(request, printer); + + // end + printer(request, "}"); + + + + return 0; +} +// TODO: move it somewhere else? +void MQTT_PublishPrinterContentsToStat(struct obk_mqtt_publishReplyPrinter_s *printer, const char *statName); +int JSON_ProcessCommandReply(const char *cmd, void *request, jsonCb_t printer, int flags) { + + if (!wal_strnicmp(cmd, "POWER", 5)) { + + printer(request, "{"); + http_tasmota_json_power(request, printer, flags); + printer(request, "}"); + if (flags == COMMAND_FLAG_SOURCE_MQTT) { + MQTT_PublishPrinterContentsToStat((struct obk_mqtt_publishReplyPrinter_s *)request, "RESULT"); + } + } + else if (!wal_strnicmp(cmd, "STATUS 8", 8) || !wal_strnicmp(cmd, "STATUS 10", 10)) { + printer(request, "{"); + http_tasmota_json_status_SNS(request, printer); + printer(request, "}"); + if (flags == COMMAND_FLAG_SOURCE_MQTT) { + MQTT_PublishPrinterContentsToStat((struct obk_mqtt_publishReplyPrinter_s *)request, "STATUS"); + } + } + else if (!wal_strnicmp(cmd, "STATUS", 6)) { + http_tasmota_json_status_generic(request, printer); + if (flags == COMMAND_FLAG_SOURCE_MQTT) { + MQTT_PublishPrinterContentsToStat((struct obk_mqtt_publishReplyPrinter_s *)request, "STATUS"); + } + } + + return 0; +} + + + diff --git a/src/mqtt/new_mqtt.c b/src/mqtt/new_mqtt.c index 45c11adef..4277babe7 100644 --- a/src/mqtt/new_mqtt.c +++ b/src/mqtt/new_mqtt.c @@ -637,8 +637,53 @@ int channelSet(obk_mqtt_request_t* request) { // Payload: echo Test1; power toggle; echo Test2 // will do echo, toggle power and do ecoh // +#define MQTT_STACK_BUFFER_SIZE 32 +#define MQTT_TOTAL_BUFFER_SIZE 4096 +typedef struct obk_mqtt_publishReplyPrinter_s { + char *allocated; + char stackBuffer[MQTT_STACK_BUFFER_SIZE]; + int curLen; +} obk_mqtt_publishReplyPrinter_t; + +void MQTT_PublishPrinterContentsToStat(struct obk_mqtt_publishReplyPrinter_s *printer, const char *statName) { + const char *toUse; + if (printer->allocated) + toUse = printer->allocated; + else + toUse = printer->stackBuffer; + MQTT_PublishStat(statName, toUse); +} +int mqtt_printf255(obk_mqtt_publishReplyPrinter_t* request, const char* fmt, ...) { + va_list argList; + char tmp[256]; + int myLen; + + memset(tmp, 0, sizeof(tmp)); + va_start(argList, fmt); + vsnprintf(tmp, 255, fmt, argList); + va_end(argList); + + myLen = strlen(tmp); + + if (request->curLen + (myLen+2) >= MQTT_STACK_BUFFER_SIZE) { + // init alloced if needed + if (request->allocated == 0) { + request->allocated = malloc(MQTT_TOTAL_BUFFER_SIZE); + strcpy(request->allocated, request->stackBuffer); + } + strcat(request->allocated, tmp); + } + else { + strcat(request->stackBuffer, tmp); + } + request->curLen += myLen; +} int tasCmnd(obk_mqtt_request_t* request) { - const char* p = request->topic; + const char *p, *args; + + obk_mqtt_publishReplyPrinter_t replyBuilder; + memset(&replyBuilder, 0, sizeof(obk_mqtt_publishReplyPrinter_t)); + p = request->topic; // TODO: better // skip to after second forward slash while (*p != '/') { if (*p == 0) return 0; p++; } @@ -647,10 +692,15 @@ int tasCmnd(obk_mqtt_request_t* request) { p++; #if 1 + args = (const char *)request->received; // I think that our function get_received always ensured that // there is a NULL terminating character after payload of MQTT // So we can feed it directly as command - CMD_ExecuteCommandArgs(p, (const char *)request->received, COMMAND_FLAG_SOURCE_MQTT); + CMD_ExecuteCommandArgs(p, args, COMMAND_FLAG_SOURCE_MQTT); + JSON_ProcessCommandReply(p, &replyBuilder, mqtt_printf255, COMMAND_FLAG_SOURCE_MQTT); + if (replyBuilder.allocated != 0) { + free(replyBuilder.allocated); + } #else int len = request->receivedLen; char copy[64]; @@ -816,7 +866,12 @@ static OBK_Publish_Result MQTT_PublishMain(mqtt_client_t* client, const char* sC { return MQTT_PublishTopicToClient(mqtt_client, CFG_GetMQTTClientId(), sChannel, sVal, flags, appendGet); } - +OBK_Publish_Result MQTT_PublishStat(const char* statName, const char* statValue) +{ + char topic[64]; + snprintf(topic,sizeof(topic),"stat/%s", CFG_GetMQTTClientId()); + return MQTT_PublishTopicToClient(mqtt_client, topic, statName, statValue, 0, false); +} /// @brief Publish a MQTT message immediately. /// @param sTopic /// @param sChannel diff --git a/src/mqtt/new_mqtt.h b/src/mqtt/new_mqtt.h index 4abff508d..276f334af 100644 --- a/src/mqtt/new_mqtt.h +++ b/src/mqtt/new_mqtt.h @@ -139,6 +139,7 @@ void MQTT_PublishOnlyDeviceChannelsIfPossible(); void MQTT_QueuePublish(const char* topic, const char* channel, const char* value, int flags); void MQTT_QueuePublishWithCommand(const char* topic, const char* channel, const char* value, int flags, PostPublishCommands command); OBK_Publish_Result MQTT_Publish(const char* sTopic, const char* sChannel, const char* value, int flags); +OBK_Publish_Result MQTT_PublishStat(const char* statName, const char* statValue); void MQTT_InvokeCommandAtEnd(PostPublishCommands command); bool MQTT_IsReady(); diff --git a/src/new_common.h b/src/new_common.h index f237df9ae..781573243 100644 --- a/src/new_common.h +++ b/src/new_common.h @@ -424,5 +424,10 @@ extern int bSafeMode; extern int g_timeSinceLastPingReply; extern int g_startPingWatchDogAfter; + +typedef int(*jsonCb_t)(void *userData, const char *fmt, ...); +int JSON_ProcessCommandReply(const char *cmd, void *request, jsonCb_t printer, int flags); + + #endif /* __NEW_COMMON_H__ */ diff --git a/src/selftest/selftest_tasmota.c b/src/selftest/selftest_tasmota.c index c5b87d3ec..b470c6de2 100644 --- a/src/selftest/selftest_tasmota.c +++ b/src/selftest/selftest_tasmota.c @@ -6,9 +6,14 @@ void Test_Tasmota_MQTT_Switch() { SIM_ClearOBK(); SIM_ClearAndPrepareForMQTTTesting("miscDevice"); + const char *my_full_device_name = "TestingDevMQTTSwitch"; + CFG_SetDeviceName(my_full_device_name); + PIN_SetPinRoleForPinIndex(9, IOR_Relay); PIN_SetPinChannelForPinIndex(9, 1); + CMD_ExecuteCommand("setChannel 1 0", 0); + SELFTEST_ASSERT_CHANNEL(1, 0); /* // If single power cmnd/tasmota_switch/Power ? // an empty message/payload sends a status query @@ -16,6 +21,18 @@ void Test_Tasmota_MQTT_Switch() { ? stat/tasmota_switch/POWER ? OFF */ SIM_SendFakeMQTTAndRunSimFrame_CMND("Power", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/miscDevice/RESULT", false); + SELFTEST_ASSERT_JSON_VALUE_STRING(0, "POWER", "OFF"); + SIM_ClearMQTTHistory(); + + CMD_ExecuteCommand("setChannel 1 1", 0); + SELFTEST_ASSERT_CHANNEL(1, 1); + + SIM_SendFakeMQTTAndRunSimFrame_CMND("Power", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/miscDevice/RESULT", false); + SELFTEST_ASSERT_JSON_VALUE_STRING(0, "POWER", "ON"); + SIM_ClearMQTTHistory(); + /* // If multiple power cmnd/tasmota_switch/Power1 ? // an empty message/payload sends a status query @@ -30,8 +47,60 @@ void Test_Tasmota_MQTT_Switch() { ? stat/tasmota_switch/RESULT ? {"POWER":"ON"} ? stat/tasmota_switch/POWER ? ON */ + SELFTEST_ASSERT_CHANNEL(1, 1); + SIM_ClearMQTTHistory(); SIM_SendFakeMQTTAndRunSimFrame_CMND("Power", "Toggle"); + SELFTEST_ASSERT_CHANNEL(1, 0); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/miscDevice/RESULT", false); + SELFTEST_ASSERT_JSON_VALUE_STRING(0, "POWER", "OFF"); + SIM_ClearMQTTHistory(); + /* + // When send status, we get full status on stat + // cmnd/tasmota_787019/STATUS + + Message 6 received on stat/tasmota_787019/STATUS at 10:36 AM: + { + "Status": { + "Module": 0, + "DeviceName": "TasmotaBathroom", + "FriendlyName": [ + "TasmotaBathroom" + ], + "Topic": "tasmota_787019", + "ButtonTopic": "0", + "Power": 0, + "PowerOnState": 3, + "LedState": 1, + "LedMask": "FFFF", + "SaveData": 1, + "SaveState": 1, + "SwitchTopic": "0", + "SwitchMode": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "ButtonRetain": 0, + "SwitchRetain": 0, + "SensorRetain": 0, + "PowerRetain": 0, + "InfoRetain": 0, + "StateRetain": 0 + } + } + */ + SIM_ClearMQTTHistory(); + SIM_SendFakeMQTTAndRunSimFrame_CMND("STATUS", ""); + SELFTEST_ASSERT_CHANNEL(1, 0); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/miscDevice/STATUS", false); + SELFTEST_ASSERT_JSON_VALUE_STRING("Status", "DeviceName", CFG_GetShortDeviceName()); + SIM_ClearMQTTHistory(); } @@ -60,12 +129,23 @@ void Test_Tasmota_MQTT_RGBCW() { ? stat/tasmota_rgbcw/RESULT ? {"POWER":"OFF"} ? stat/tasmota_switch/POWER ? OFF */ + CMD_ExecuteCommand("led_enableAll 0", 0); + SIM_SendFakeMQTTAndRunSimFrame_CMND("Power", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/rgbcwBulb/RESULT", false); + SELFTEST_ASSERT_JSON_VALUE_STRING(0, "POWER", "OFF"); + SIM_ClearMQTTHistory(); + CMD_ExecuteCommand("led_enableAll 1", 0); + + SIM_SendFakeMQTTAndRunSimFrame_CMND("Power", ""); + SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("stat/rgbcwBulb/RESULT", false); + SELFTEST_ASSERT_JSON_VALUE_STRING(0, "POWER", "ON"); + SIM_ClearMQTTHistory(); /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/CT 153 - gives + gives stat/tasmota_rgbcw/RESULT { "POWER": "ON", "Dimmer": 100, @@ -86,7 +166,7 @@ void Test_Tasmota_MQTT_RGBCW() { /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/CT - gives + gives stat/tasmota_rgbcw/RESULT { "CT": 153 } @@ -95,7 +175,7 @@ void Test_Tasmota_MQTT_RGBCW() { /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/HSBColor - gives + gives stat/tasmota_rgbcw/RESULT { "POWER": "ON", "Dimmer": 100, @@ -115,7 +195,7 @@ void Test_Tasmota_MQTT_RGBCW() { /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/HSBColor 90,100,0 - gives + gives stat/tasmota_rgbcw/RESULT { "POWER": "ON", "Dimmer": 100, @@ -135,7 +215,7 @@ void Test_Tasmota_MQTT_RGBCW() { /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/Dimmer - gives + gives stat/tasmota_rgbcw/RESULT { "Dimmer": 20 } @@ -144,7 +224,7 @@ void Test_Tasmota_MQTT_RGBCW() { /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/Dimmer 20 - gives + gives stat/tasmota_rgbcw/RESULT { "POWER": "ON", "Dimmer": 20, @@ -164,7 +244,7 @@ void Test_Tasmota_MQTT_RGBCW() { /* // Powers as for single relay, but also cmnd/tasmota_rgbcw/Dimmer 100 - gives + gives stat/tasmota_rgbcw/RESULT { "POWER": "ON", "Dimmer": 100,