mirror of
https://github.com/openshwprojects/OpenBK7231T_App.git
synced 2025-10-29 11:33:20 +00:00
Add ChType_Enum and enable SetChannelEnum. (#1830)
* create a ChType_Enum to go with SetChannelEnum * resolve build errors for ChType_Enum PR * fixing build errors for simulator and others for cmd_enums.c * added ChType_ReadOnlyEnum and assocaited enum selftests * ChType_Enum simulation and memory error corrections * ChType_Enum documentation updates --------- Co-authored-by: root <root@stonacek.nz>
This commit is contained in:
parent
993a46a19d
commit
4ad9a85a08
@ -1355,3 +1355,81 @@ return
|
||||
```
|
||||
|
||||
|
||||
### Example use of Channel Enum Types in combination with TuyaMCU dpids
|
||||
|
||||
Example uses of `SetChannelType [ch] Enum` and `SetChannelType [ch] ReadOnlyEnum` combined `SetChannelEnum [ch] [option:label]`
|
||||
|
||||
From [CB2S - Tongou MCB Temperature and Humidity relay](https://www.elektroda.com/rtvforum/viewtopic.php?p=21125206#21125206)
|
||||
<br>
|
||||
|
||||
```c++
|
||||
// tuyaMCU store RAW data in /cm?cmnd=Dp must be turned off on this device..
|
||||
setflag 46 0
|
||||
|
||||
ntp_setServer 132.163.97.4
|
||||
ntp_timeZoneOfs 12:00
|
||||
|
||||
startDriver TuyaMCU
|
||||
tuyaMcu_setBaudRate 115200
|
||||
// always report paired
|
||||
tuyaMcu_defWiFiState 4
|
||||
|
||||
// update states any time the temperature changes
|
||||
addEventHandler OnChannelChange 27 tuyaMcu_sendQueryState
|
||||
|
||||
// 2 switch 1 relay bool - 121 device control must be 2 or 3 (remote mode)
|
||||
setChannelType 21 Toggle
|
||||
setChannelLabel 21 "Switch 1"
|
||||
linkTuyaMCUOutputToChannel 2 bool 21
|
||||
|
||||
// 101 switch 2 relay bool - 121 device control must be 2 or 3 (remote mode)
|
||||
setChannelType 22 Toggle
|
||||
setChannelLabel 22 "Switch 2"
|
||||
linkTuyaMCUOutputToChannel 101 bool 22
|
||||
|
||||
// 27 current temperature /10 - dpId 20 changes C/F
|
||||
setChannelType 1 Temperature_div10
|
||||
setChannelLabel 1 temperature
|
||||
linkTuyaMCUOutputToChannel 27 val 1
|
||||
|
||||
// 46 current humidity
|
||||
setChannelType 2 Humidity
|
||||
setChannelLabel 2 Humidity
|
||||
linkTuyaMCUOutputToChannel 46 val 2
|
||||
|
||||
// 118 event RO
|
||||
setChannelType 3 ReadOnlyEnum
|
||||
setChannelLabel 3 "Event Status"
|
||||
SetChannelEnum 3 0:Normal "9:Buttons Locked" "10:Local Mode" "11:Remote Control" "12:Any Control"
|
||||
linkTuyaMCUOutputToChannel 118 enum 3
|
||||
|
||||
//102 online state enum; 0 online, 1 offline
|
||||
setChannelType 4 ReadOnlyEnum
|
||||
setChannelLabel 4 "Online State"
|
||||
setChannelEnum 4 0:Online 1:Offline
|
||||
linkTuyaMCUOutputToChannel 102 enum 4
|
||||
|
||||
// 121 device control mode enum; 0 local_lock, 1 MCU control, 2 OBK control, 3 MCU and Tuya control
|
||||
setChannelType 5 Enum
|
||||
setChannelLabel 5 "Device Control"
|
||||
SetChannelEnum 5 "0:Buttons Locked" "1:Device Control" "2:Remote Control" "3:Any Control"
|
||||
linkTuyaMCUOutputToChannel 121 enum 5
|
||||
|
||||
// 106 device Power-On Relay behaviour
|
||||
setChannelType 6 Enum
|
||||
setChannelLabel 6 "Power-on Behaviour"
|
||||
SetChannelEnum 6 0:off 1:on 2:memory
|
||||
linkTuyaMCUOutputToChannel 106 enum 6
|
||||
|
||||
// 107 Switch 1 Automatic Control Mode
|
||||
setChannelType 7 Enum
|
||||
setChannelLabel 7 "Switch 1 Control Mode"
|
||||
setChannelEnum 7 0:Temp 1:Humidity
|
||||
linkTuyaMCUOutputToChannel 107 enum 7
|
||||
|
||||
//trunacted for example
|
||||
|
||||
// refresh tuyaMCU after definitions
|
||||
tuyaMcu_sendQueryState
|
||||
|
||||
```
|
||||
|
||||
@ -66,4 +66,6 @@ Do not add anything here, as it will overwritten with next rebuild.
|
||||
| Frequency_div1000 | For TuyaMCU power metering. Not used for BL09** and CSE** sensors. Divider is used by TuyaMCU, because TuyaMCU sends always values as integers so we have to divide them before displaying on UI |
|
||||
| OpenStopClose | TODO |
|
||||
| Percent | TODO |
|
||||
| Enum | This channel type allows creating custom Enum types in combination with SetChannelEnum command. Ideal for defining TuyaMCU enum mappings. |
|
||||
| ReadOnlyEnum | Read Only Enum Channel type for use in combination with SetChannelEnum command. Ideal for defining TuyaMCU enum mappings. |
|
||||
| Max | This is the current total number of available channel types. |
|
||||
|
||||
@ -232,7 +232,7 @@ Do not add anything here, as it will overwritten with next rebuild.
|
||||
| setButtonLabel | [ButtonIndex][Label] | Sets the label of custom scriptable HTTP page button.<br/><br/>See also [setButtonLabel on forum](https://www.elektroda.com/rtvforum/find.php?q=setButtonLabel). | File: driver/drv_httpButtons.c<br/>Function: CMD_setButtonLabel |
|
||||
| setButtonTimes | [ValLongPress][ValShortPress][ValRepeat] | Each value is times 100ms, so: SetButtonTimes 2 1 1 means 200ms long press, 100ms short and 100ms repeat.<br/><br/>See also [setButtonTimes on forum](https://www.elektroda.com/rtvforum/find.php?q=setButtonTimes). | File: new_pins.c<br/>Function: CMD_SetButtonTimes |
|
||||
| SetChannel | [ChannelIndex][ChannelValue] | Sets a raw channel to given value. Relay channels are using 1 and 0 values. PWM channels are within [0,100] range. Do not use this for LED control, because there is a better and more advanced LED driver with dimming and configuration memory (remembers setting after on/off), LED driver commands has 'led_' prefix.<br/><br/>See also [SetChannel on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannel). | File: cmnds/cmd_channels.c<br/>Function: CMD_SetChannel |
|
||||
| SetChannelEnum | [ChannelIndex][Value,Title][Value,Title] | Creates a custom channel enumeration.<br/><br/>See also [SetChannelEnum on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelEnum). | File: cmnds/cmd_channels.c<br/>Function: SetChannelEnum |
|
||||
| SetChannelEnum | [ChannelIndex][Value:Title][Value:Title] | Creates a channel enumeration type. Channel type must be set to Enum. e.g. SetChannelEnum 1:One "2:Enum Two" 5:Five.<br/><br/>See also [SetChannelEnum on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelEnum). | File: cmnds/cmd_channels.c<br/>Function: SetChannelEnum |
|
||||
| SetChannelFloat | [ChannelIndex][ChannelValue] | Sets a raw channel to given float value. Currently only used for LED PWM channels.<br/><br/>See also [SetChannelFloat on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelFloat). | File: cmnds/cmd_channels.c<br/>Function: CMD_SetChannelFloat |
|
||||
| SetChannelLabel | [ChannelIndex][Str][bHideTogglePrefix] | Sets a channel label for UI and default entity name for Home Assistant discovery. If you use 1 for bHideTogglePrefix, then the 'Toggle ' prefix from UI button will be omitted.<br/><br/>See also [SetChannelLabel on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelLabel). | File: cmnds/cmd_channels.c<br/>Function: CMD_SetChannelLabel |
|
||||
| SetChannelPrivate | [ChannelIndex][bPrivate] | Channels marked as private are NEVER published via MQTT and excluded from Home Assistant discovery.<br/><br/>See also [SetChannelPrivate on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelPrivate). | File: cmnds/cmd_channels.c<br/>Function: NULL); |
|
||||
|
||||
@ -235,7 +235,7 @@ Do not add anything here, as it will overwritten with next rebuild.
|
||||
| setButtonLabel | [ButtonIndex][Label] | Sets the label of custom scriptable HTTP page button.<br/><br/>See also [setButtonLabel on forum](https://www.elektroda.com/rtvforum/find.php?q=setButtonLabel). |
|
||||
| setButtonTimes | [ValLongPress][ValShortPress][ValRepeat] | Each value is times 100ms, so: SetButtonTimes 2 1 1 means 200ms long press, 100ms short and 100ms repeat.<br/><br/>See also [setButtonTimes on forum](https://www.elektroda.com/rtvforum/find.php?q=setButtonTimes). |
|
||||
| SetChannel | [ChannelIndex][ChannelValue] | Sets a raw channel to given value. Relay channels are using 1 and 0 values. PWM channels are within [0,100] range. Do not use this for LED control, because there is a better and more advanced LED driver with dimming and configuration memory (remembers setting after on/off), LED driver commands has 'led_' prefix.<br/><br/>See also [SetChannel on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannel). |
|
||||
| SetChannelEnum | [ChannelIndex][Value,Title][Value,Title] | Creates a custom channel enumeration.<br/><br/>See also [SetChannelEnum on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelEnum). |
|
||||
| SetChannelEnum | [ChannelIndex][Value:Title][Value:Title] | Creates a channel enumeration type. Channel type must be set to Enum or ReadOnlyEnum. e.g. SetChannelEnum 1:One "2:Enum Two" 5:Five.<br/><br/>See also [SetChannelEnum on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelEnum). |
|
||||
| SetChannelFloat | [ChannelIndex][ChannelValue] | Sets a raw channel to given float value. Currently only used for LED PWM channels.<br/><br/>See also [SetChannelFloat on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelFloat). |
|
||||
| SetChannelLabel | [ChannelIndex][Str][bHideTogglePrefix] | Sets a channel label for UI and default entity name for Home Assistant discovery. If you use 1 for bHideTogglePrefix, then the 'Toggle ' prefix from UI button will be omitted.<br/><br/>See also [SetChannelLabel on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelLabel). |
|
||||
| SetChannelPrivate | [ChannelIndex][bPrivate] | Channels marked as private are NEVER published via MQTT and excluded from Home Assistant discovery.<br/><br/>See also [SetChannelPrivate on forum](https://www.elektroda.com/rtvforum/find.php?q=SetChannelPrivate). |
|
||||
|
||||
@ -495,6 +495,22 @@
|
||||
"file": "new_pins.h",
|
||||
"driver": ""
|
||||
},
|
||||
{
|
||||
"name": "Enum",
|
||||
"title": "Enum",
|
||||
"descr": "This channel type allows creating custom Enum types in combination with SetChannelEnum command. Ideal for defining TuyaMCU enum mappings.",
|
||||
"enum": "ChType_Enum",
|
||||
"file": "new_pins.h",
|
||||
"driver": ""
|
||||
},
|
||||
{
|
||||
"name": "ReadOnlyEnum",
|
||||
"title": "ReadOnlyEnum",
|
||||
"descr": "Read Only Enum Channel type for use in combination with SetChannelEnum command. Ideal for defining TuyaMCU enum mappings.",
|
||||
"enum": "ChType_ReadOnlyEnum",
|
||||
"file": "new_pins.h",
|
||||
"driver": ""
|
||||
},
|
||||
{
|
||||
"name": "Max",
|
||||
"title": "TODO",
|
||||
@ -503,4 +519,4 @@
|
||||
"file": "new_pins.h",
|
||||
"driver": ""
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@ -2044,8 +2044,8 @@
|
||||
},
|
||||
{
|
||||
"name": "SetChannelEnum",
|
||||
"args": "[ChannelIndex][Value,Title][Value,Title]",
|
||||
"descr": "Creates a custom channel enumeration.",
|
||||
"args": "[ChannelIndex][Value:Title][Value:Title]",
|
||||
"descr": "Creates a channel enumeration type. Channel type must be set to Enum or ReadOnlyEnum. e.g. SetChannelEnum 1:One \"2:Enum Two\" 5:Five",
|
||||
"fn": "SetChannelEnum",
|
||||
"file": "cmnds/cmd_channels.c",
|
||||
"requires": "",
|
||||
@ -3257,4 +3257,4 @@
|
||||
"requires": "",
|
||||
"examples": ""
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@ -179,6 +179,7 @@
|
||||
<ClCompile Include="src\cJSON\cJSON.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_berry.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_channels.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_enums.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_eventHandlers.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_if.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_main.c" />
|
||||
@ -783,6 +784,7 @@
|
||||
<ClCompile Include="src\selftest\selftest_demo_signAndValue.c" />
|
||||
<ClCompile Include="src\selftest\selftest_doorSensor.c" />
|
||||
<ClCompile Include="src\selftest\selftest_flashSearch.c" />
|
||||
<ClCompile Include="src\selftest\selftest_enums.c" />
|
||||
<ClCompile Include="src\selftest\selftest_hass_discovery_base.c" />
|
||||
<ClCompile Include="src\selftest\selftest_hass_discovery_ext.c" />
|
||||
<ClCompile Include="src\selftest\selftest_http_led.c" />
|
||||
@ -1305,4 +1307,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
<ClCompile Include="src\bitmessage\bitmessage_write.c" />
|
||||
<ClCompile Include="src\cJSON\cJSON.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_channels.c" />
|
||||
<ClCompile Include="src\selftest\selftest_enums.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_eventHandlers.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_if.c" />
|
||||
<ClCompile Include="src\cmnds\cmd_main.c" />
|
||||
@ -655,4 +656,4 @@
|
||||
<CustomBuild Include="src\rgb2hsv.h" />
|
||||
<CustomBuild Include="..\..\platforms\bk7231t\bk7231t_os\application.mk" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@ -6,6 +6,7 @@ set(OBKM_SRC
|
||||
${OBK_SRCS}bitmessage/bitmessage_write.c
|
||||
${OBK_SRCS}cmnds/cmd_berry.c
|
||||
${OBK_SRCS}cmnds/cmd_channels.c
|
||||
${OBK_SRCS}cmnds/cmd_enums.c
|
||||
${OBK_SRCS}cmnds/cmd_eventHandlers.c
|
||||
${OBK_SRCS}cmnds/cmd_if.c
|
||||
${OBK_SRCS}cmnds/cmd_main.c
|
||||
|
||||
@ -21,6 +21,7 @@ OBKM_SRC += $(OBK_SRCS)bitmessage/bitmessage_write.c
|
||||
OBKM_SRC += $(OBK_SRCS)cJSON/cJSON.c
|
||||
OBKM_SRC += $(OBK_SRCS)cmnds/cmd_berry.c
|
||||
OBKM_SRC += $(OBK_SRCS)cmnds/cmd_channels.c
|
||||
OBKM_SRC += $(OBK_SRCS)cmnds/cmd_enums.c
|
||||
OBKM_SRC += $(OBK_SRCS)cmnds/cmd_eventHandlers.c
|
||||
OBKM_SRC += $(OBK_SRCS)cmnds/cmd_if.c
|
||||
OBKM_SRC += $(OBK_SRCS)cmnds/cmd_main.c
|
||||
|
||||
@ -18,6 +18,7 @@ int g_doNotPublishChannels = 0;
|
||||
|
||||
void CHANNEL_FreeLabels() {
|
||||
for (int ch = 0; ch < CHANNEL_MAX; ch++) {
|
||||
CMD_FreeLabels(); // free any enum labels
|
||||
if (g_channelLabels[ch]) {
|
||||
free(g_channelLabels[ch]);
|
||||
g_channelLabels[ch] = 0;
|
||||
@ -592,13 +593,11 @@ void CMD_InitChannelCommands(){
|
||||
//cmddetail:"fn":"CMD_FullBootTime","file":"cmnds/cmd_channels.c","requires":"",
|
||||
//cmddetail:"examples":""}
|
||||
CMD_RegisterCommand("FullBootTime", CMD_FullBootTime, NULL);
|
||||
//cmddetail:{"name":"SetChannelEnum","args":"[ChannelIndex][Value,Title][Value,Title]",
|
||||
//cmddetail:"descr":"Creates a custom channel enumeration.",
|
||||
//cmddetail:{"name":"SetChannelEnum","args":"[ChannelIndex][Value:Title][Value:Title]",
|
||||
//cmddetail:"descr":"Creates a channel enumeration type. Channel type must be set to Enum or ReadOnlyEnum. e.g. SetChannelEnum 1:One \"2:Enum Two\" 5:Five",
|
||||
//cmddetail:"fn":"SetChannelEnum","file":"cmnds/cmd_channels.c","requires":"",
|
||||
//cmddetail:"examples":""}
|
||||
#if WINDOWS
|
||||
//CMD_RegisterCommand("SetChannelEnum", CMD_SetChannelEnum, NULL);
|
||||
#endif
|
||||
CMD_RegisterCommand("SetChannelEnum", CMD_SetChannelEnum, NULL);
|
||||
//cmddetail:{"name":"SetChannelLabel","args":"[ChannelIndex][Str][bHideTogglePrefix]",
|
||||
//cmddetail:"descr":"Sets a channel label for UI and default entity name for Home Assistant discovery. If you use 1 for bHideTogglePrefix, then the 'Toggle ' prefix from UI button will be omitted",
|
||||
//cmddetail:"fn":"CMD_SetChannelLabel","file":"cmnds/cmd_channels.c","requires":"",
|
||||
|
||||
148
src/cmnds/cmd_enums.c
Normal file
148
src/cmnds/cmd_enums.c
Normal file
@ -0,0 +1,148 @@
|
||||
|
||||
#include "../logging/logging.h"
|
||||
#include "../new_pins.h"
|
||||
#include "../new_cfg.h"
|
||||
#include "../obk_config.h"
|
||||
#include "../driver/drv_public.h"
|
||||
#include <ctype.h>
|
||||
#include "cmd_local.h"
|
||||
#include "cmd_enums.h"
|
||||
|
||||
#define CMD_ENUM_MAX_LABEL_SIZE 32
|
||||
|
||||
channelEnum_t **g_enums = 0;
|
||||
|
||||
void CMD_FreeChannelEnumOptions(int ch) {
|
||||
if (g_enums && g_enums[ch] && g_enums[ch]->numOptions != 0) {
|
||||
for (int i = 0; i < g_enums[ch]->numOptions; i++) {
|
||||
os_free(g_enums[ch]->options[i].label);
|
||||
}
|
||||
os_free(g_enums[ch]->options);
|
||||
g_enums[ch]->numOptions=0;
|
||||
os_free(g_enums[ch]);
|
||||
g_enums[ch] = 0;
|
||||
}
|
||||
}
|
||||
// method to clean up on shutdown
|
||||
void CMD_FreeLabels() {
|
||||
for (int ch = 0; ch < CHANNEL_MAX; ch++) {
|
||||
// if (g_enums && g_enums[ch] && g_enums[ch]->numOptions != 0) {
|
||||
CMD_FreeChannelEnumOptions(ch);
|
||||
//os_free(g_enums[ch]);
|
||||
}
|
||||
//os_free(g_enums);
|
||||
//g_enums = 0;
|
||||
|
||||
}
|
||||
|
||||
// helpers for preparing a homeassistant select template for enums
|
||||
void CMD_GenEnumValueTemplate(channelEnum_t *e, char *out, int outSize) {
|
||||
CMD_FormatEnumTemplate(e, out, outSize, false);
|
||||
}
|
||||
void CMD_GenEnumCommandTemplate(channelEnum_t *e, char *out, int outSize) {
|
||||
CMD_FormatEnumTemplate(e, out, outSize, true);
|
||||
}
|
||||
|
||||
// this is quite similar to hass.c generate_command_template,
|
||||
// except for enum types are not ordered and don't align to an array index
|
||||
// we also want a default for undefined enums
|
||||
// in the future this could be merge with those in hass.c
|
||||
void CMD_FormatEnumTemplate(channelEnum_t *e, char *out,
|
||||
int outSize, bool isCommand) {
|
||||
char tmp[CMD_ENUM_MAX_LABEL_SIZE+24];
|
||||
int numOptions = 0;
|
||||
*out = 0;
|
||||
strcat_safe(out, "{{ {", outSize);
|
||||
if (e != NULL && e != 0)
|
||||
numOptions = e->numOptions;
|
||||
|
||||
for (int i = 0; i < numOptions; i++) {
|
||||
if (!isCommand)
|
||||
sprintf(tmp, "%i:'%s', ", e->options[i].value,e->options[i].label);
|
||||
else
|
||||
sprintf(tmp, "'%s':'%i', ", e->options[i].label,e->options[i].value);
|
||||
|
||||
strcat_safe(out, tmp, outSize);
|
||||
}
|
||||
if (!isCommand) {
|
||||
strcat_safe(out, "99999:'Undefined'}", outSize);
|
||||
strcat_safe(out,"[(value | int(99999))] | default(\"Undefined Enum [\"~value~\"]\") }}",outSize);
|
||||
} else {
|
||||
strcat_safe(out, "'Undefined':'99999'}", outSize);
|
||||
strcat_safe(out,"[value] | default(99999) }}",outSize);
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
FILE *f = fopen("lastEnumTemplate.txt", "w");
|
||||
fprintf(f, out);
|
||||
fclose(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
commandResult_t CMD_SetChannelEnum(const void *context, const char *cmd,
|
||||
const char *args, int cmdFlags) {
|
||||
int ch;
|
||||
const char *s;
|
||||
char *label;
|
||||
channelEnum_t *en;
|
||||
|
||||
Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES);
|
||||
// following check must be done after 'Tokenizer_TokenizeString',
|
||||
// so we know arguments count in Tokenizer. 'cmd' argument is
|
||||
// only for warning display
|
||||
if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 2)) {
|
||||
return CMD_RES_NOT_ENOUGH_ARGUMENTS;
|
||||
}
|
||||
|
||||
ch = Tokenizer_GetArgInteger(0);
|
||||
|
||||
if (g_enums == 0) {
|
||||
g_enums = malloc(sizeof(channelEnum_t*)*CHANNEL_MAX);
|
||||
memset(g_enums,0, sizeof(channelEnum_t*)*CHANNEL_MAX);
|
||||
}
|
||||
|
||||
if (g_enums[ch] != 0 && g_enums[ch]->numOptions != 0) {
|
||||
// free any previously defined channel enums
|
||||
CMD_FreeChannelEnumOptions(ch);
|
||||
}
|
||||
en = malloc(sizeof(channelEnum_t));
|
||||
g_enums[ch] = en;
|
||||
|
||||
en->numOptions = Tokenizer_GetArgsCount()-1;
|
||||
en->options = malloc(sizeof(channelEnumOption_t)*en->numOptions);
|
||||
for (int i = 0; i < en->numOptions; i++) {
|
||||
s = Tokenizer_GetArg(1+i);
|
||||
en->options[i].value = atoi(s);
|
||||
while (*s) {
|
||||
if (*s == ':') {
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
//en->options[i].label = strdup(s);
|
||||
int llen = strlen(s) > CMD_ENUM_MAX_LABEL_SIZE ? CMD_ENUM_MAX_LABEL_SIZE : strlen(s);
|
||||
label = (char *)malloc(llen+1);
|
||||
strncpy(label,s,llen);
|
||||
label[llen]='\0';
|
||||
en->options[i].label = label;
|
||||
}
|
||||
return CMD_RES_OK;
|
||||
}
|
||||
|
||||
// helper function to map enum values to labels
|
||||
char* CMD_FindChannelEnumLabel(channelEnum_t *channelEnum, int value) {
|
||||
static char notfound[5]; // assuming INT_MAX 32k
|
||||
snprintf(notfound, 5, "%i", value); // if no label defined, use the value
|
||||
if (channelEnum == NULL || channelEnum == 0 || channelEnum->numOptions == 0 ) { // g_enum zeroed on init
|
||||
return notfound;
|
||||
}
|
||||
|
||||
for (int i = 0; i < channelEnum->numOptions; i++) {
|
||||
if (channelEnum->options[i].value == value)
|
||||
return channelEnum->options[i].label;
|
||||
}
|
||||
return notfound;
|
||||
}
|
||||
|
||||
@ -16,9 +16,11 @@ typedef struct channelEnum_s {
|
||||
|
||||
extern channelEnum_t **g_enums;
|
||||
|
||||
void CMD_GenEnumValueTemplate(channelEnum_t *e, char *out, int outSize);
|
||||
void CMD_GenEnumCommandTemplate(channelEnum_t *e, char *out, int outSize);
|
||||
void CMD_FormatEnumTemplate(channelEnum_t *e, char *out, int outSize, bool isCommand);
|
||||
|
||||
void CMD_FormatEnumTemplate(channelEnum_t *e, char *out, int outSize);
|
||||
|
||||
char* CMD_FindChannelEnumLabel(channelEnum_t *channelEnum, int value);
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ commandResult_t CMD_If(const void *context, const char *cmd, const char *args, i
|
||||
void CMD_ExpandConstantsWithinString(const char *in, char *out, int outLen);
|
||||
void CMD_Script_ProcessWaitersForEvent(byte eventCode, int argument);
|
||||
bool CheckEventCondition(eventWait_t *w, byte eventCode, int argument);
|
||||
void CMD_FreeLabels();
|
||||
|
||||
|
||||
#endif // __CMD_LOCAL_H__
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
|
||||
#include "../logging/logging.h"
|
||||
#include "../new_pins.h"
|
||||
#include "../new_cfg.h"
|
||||
#include "../obk_config.h"
|
||||
#include "../driver/drv_public.h"
|
||||
#include <ctype.h>
|
||||
#include "cmd_local.h"
|
||||
#include "cmd_newEnums.h"
|
||||
|
||||
channelEnum_t **g_enums = 0;
|
||||
|
||||
void CMD_FormatEnumTemplate(channelEnum_t *e, char *out, int outSize) {
|
||||
char tmp[8];
|
||||
*out = 0;
|
||||
for (int i = 0; i < e->numOptions; i++) {
|
||||
strcat_safe(out, "{% ", outSize);
|
||||
if (i == 0) {
|
||||
strcat_safe(out, "if", outSize);
|
||||
}
|
||||
else {
|
||||
strcat_safe(out, "elif", outSize);
|
||||
}
|
||||
strcat_safe(out, " value == '", outSize);
|
||||
sprintf(tmp, "%i", e->options[i].value);
|
||||
strcat_safe(out, tmp, outSize);
|
||||
strcat_safe(out, "' %}\n", outSize);
|
||||
strcat_safe(out, " ", outSize);
|
||||
strcat_safe(out, e->options[i].label, outSize);
|
||||
strcat_safe(out, "\n", outSize);
|
||||
}
|
||||
strcat_safe(out, "{% else %}\n", outSize);
|
||||
strcat_safe(out, " Unknown\n", outSize);
|
||||
strcat_safe(out, "{% endif %}", outSize);
|
||||
#if WINDOWS
|
||||
FILE *f = fopen("lastEnumTemplate.txt", "w");
|
||||
fprintf(f, out);
|
||||
fclose(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
commandResult_t CMD_SetChannelEnum(const void *context, const char *cmd,
|
||||
const char *args, int cmdFlags) {
|
||||
int ch;
|
||||
const char *s;
|
||||
|
||||
Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES);
|
||||
// following check must be done after 'Tokenizer_TokenizeString',
|
||||
// so we know arguments count in Tokenizer. 'cmd' argument is
|
||||
// only for warning display
|
||||
if (Tokenizer_CheckArgsCountAndPrintWarning(cmd, 2)) {
|
||||
return CMD_RES_NOT_ENOUGH_ARGUMENTS;
|
||||
}
|
||||
|
||||
ch = Tokenizer_GetArgInteger(0);
|
||||
|
||||
if (g_enums == 0) {
|
||||
g_enums = malloc(sizeof(channelEnum_t*)*CHANNEL_MAX);
|
||||
memset(g_enums,0, sizeof(channelEnum_t*)*CHANNEL_MAX);
|
||||
}
|
||||
channelEnum_t *en = malloc(sizeof(channelEnum_t));
|
||||
g_enums[ch] = en;
|
||||
en->numOptions = Tokenizer_GetArgsCount()-1;
|
||||
en->options = malloc(sizeof(channelEnumOption_t)*en->numOptions);
|
||||
for (int i = 0; i < en->numOptions; i++) {
|
||||
s = Tokenizer_GetArg(1+i);
|
||||
en->options[i].value = atoi(s);
|
||||
while (*s) {
|
||||
if (*s == ':') {
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
en->options[i].label = strdup(s);
|
||||
}
|
||||
return CMD_RES_OK;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "../hal/hal_wifi.h"
|
||||
#include "../driver/drv_public.h"
|
||||
#include "../new_pins.h"
|
||||
#include "../cmnds/cmd_enums.h"
|
||||
|
||||
#if ENABLE_HA_DISCOVERY
|
||||
|
||||
@ -134,6 +135,9 @@ void hass_populate_unique_id(ENTITY_TYPE type, int index, char* uniq_id, int ase
|
||||
case HASS_BUTTON:
|
||||
sprintf(uniq_id, "%s_%s", longDeviceName, "button");
|
||||
break;
|
||||
case HASS_SELECT:
|
||||
sprintf(uniq_id, "%s_%s", longDeviceName, "select");
|
||||
break;
|
||||
default:
|
||||
// TODO: USE type here as well?
|
||||
// If type is not set, and we use "sensor" naming, we can easily make collision
|
||||
@ -322,6 +326,19 @@ static void generate_command_template(int numoptions, const char* options[], cha
|
||||
|
||||
HassDeviceInfo* hass_createSelectEntityIndexed(const char* state_topic, const char* command_topic, int numoptions,
|
||||
const char* options[], const char* title) {
|
||||
|
||||
char value_template[512];
|
||||
generate_value_template(numoptions, options, value_template, sizeof(value_template));
|
||||
|
||||
char command_template[512];
|
||||
generate_command_template(numoptions, options, command_template, sizeof(command_template));
|
||||
|
||||
return hass_createSelectEntityIndexedCustom(state_topic, command_topic, numoptions, options, title,
|
||||
value_template, command_template);
|
||||
}
|
||||
|
||||
HassDeviceInfo* hass_createSelectEntityIndexedCustom(const char* state_topic, const char* command_topic, int numoptions,
|
||||
const char* options[], const char* title, char* value_template, char* command_template) {
|
||||
HassDeviceInfo* info = hass_init_device_info(HASS_SELECT, 0, NULL, NULL, 0, title);
|
||||
|
||||
cJSON_AddStringToObject(info->root, "name", title);
|
||||
@ -335,17 +352,14 @@ HassDeviceInfo* hass_createSelectEntityIndexed(const char* state_topic, const ch
|
||||
}
|
||||
cJSON_AddItemToObject(info->root, "options", select_options);
|
||||
|
||||
char value_template[512];
|
||||
generate_value_template(numoptions, options, value_template, sizeof(value_template));
|
||||
cJSON_AddStringToObject(info->root, "value_template", value_template);
|
||||
|
||||
char command_template[512];
|
||||
generate_command_template(numoptions, options, command_template, sizeof(command_template));
|
||||
cJSON_AddStringToObject(info->root, "command_template", command_template);
|
||||
|
||||
cJSON_AddStringToObject(info->root, "availability_topic", "~/status");
|
||||
cJSON_AddStringToObject(info->root, "payload_available", "online");
|
||||
cJSON_AddStringToObject(info->root, "payload_not_available", "offline");
|
||||
if (!CFG_HasFlag(OBK_FLAG_NOT_PUBLISH_AVAILABILITY)) {
|
||||
cJSON_AddStringToObject(info->root, "availability_topic", "~/connected");
|
||||
cJSON_AddStringToObject(info->root, "payload_available", "online");
|
||||
cJSON_AddStringToObject(info->root, "payload_not_available", "offline");
|
||||
}
|
||||
|
||||
sprintf(info->channel, "select/%s/config", info->unique_id);
|
||||
|
||||
@ -572,7 +586,7 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* p
|
||||
case HASS_BUTTON:
|
||||
sprintf(g_hassBuffer, "%s" , "");
|
||||
break;
|
||||
|
||||
case HASS_READONLYENUM:
|
||||
default:
|
||||
sprintf(g_hassBuffer, "%s", CHANNEL_GetLabel(index));
|
||||
break;
|
||||
@ -607,6 +621,12 @@ HassDeviceInfo* hass_init_device_info(ENTITY_TYPE type, int index, const char* p
|
||||
}
|
||||
}
|
||||
|
||||
if (type == HASS_READONLYENUM) {
|
||||
char value_template[1024];
|
||||
CMD_GenEnumValueTemplate(g_enums[index], value_template, sizeof(value_template));
|
||||
cJSON_AddStringToObject(info->root, "value_template", value_template);
|
||||
}
|
||||
|
||||
cJSON_AddStringToObject(info->root, "uniq_id", info->unique_id); //unique_id
|
||||
cJSON_AddNumberToObject(info->root, "qos", 1);
|
||||
|
||||
@ -1012,6 +1032,11 @@ HassDeviceInfo* hass_init_sensor_device_info(ENTITY_TYPE type, int channel, int
|
||||
sprintf(g_hassBuffer, "~/%d/get", channel);
|
||||
cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer);
|
||||
break;
|
||||
case HASS_READONLYENUM:
|
||||
sprintf(g_hassBuffer, "~/%d/get", channel);
|
||||
cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer);
|
||||
// str sensor can't have state_class, so return before it gets set
|
||||
return info;
|
||||
case CUSTOM_SENSOR:
|
||||
sprintf(g_hassBuffer, "~/%d/get", channel);
|
||||
cJSON_AddStringToObject(info->root, "stat_t", g_hassBuffer);
|
||||
|
||||
@ -101,6 +101,8 @@ typedef enum {
|
||||
HASS_PERCENT,
|
||||
HASS_TEXTFIELD,
|
||||
HASS_BUTTON,
|
||||
// @Brief ChType_ReadOnlyEnum, readonly with value_template
|
||||
HASS_READONLYENUM,
|
||||
} ENTITY_TYPE;
|
||||
|
||||
typedef enum {
|
||||
@ -145,6 +147,8 @@ HassDeviceInfo* hass_createSelectEntity(const char* state_topic, const char* com
|
||||
const char* options[], const char* title);
|
||||
HassDeviceInfo* hass_createSelectEntityIndexed(const char* state_topic, const char* command_topic, int numoptions,
|
||||
const char* options[], const char* title);
|
||||
HassDeviceInfo* hass_createSelectEntityIndexedCustom(const char* state_topic, const char* command_topic, int numoptions,
|
||||
const char* options[], const char* title, char* value_template, char* command_template);
|
||||
|
||||
HassDeviceInfo* hass_createToggle(const char *label, const char *stateTopic, const char *commandTopic);
|
||||
HassDeviceInfo* hass_init_textField_info(int index);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#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_public.h"
|
||||
#include "../driver/drv_bl_shared.h"
|
||||
@ -495,6 +496,56 @@ int http_fn_index(http_request_t* request) {
|
||||
hprintf255(request, "Channel %s = %i", CHANNEL_GetLabel(i), iValue);
|
||||
}
|
||||
poststr(request, "</td></tr>");
|
||||
} 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, "<tr><td>");
|
||||
hprintf255(request, "<p>Change channel %s enum:</p><form action=\"index\">", CHANNEL_GetLabel(i));
|
||||
hprintf255(request, "<input type=\"hidden\" name=\"setIndex\" value=\"%i\">", i);
|
||||
hprintf255(request, "<input type=\"number\" name=\"set\" value=\"%i\" onblur=\"this.form.submit()\">", iValue);
|
||||
hprintf255(request, "<input type=\"submit\" value=\"Set!\"/></form>");
|
||||
hprintf255(request, "</form>");
|
||||
poststr(request, "</td></tr>");
|
||||
} else {
|
||||
en = g_enums[i];
|
||||
|
||||
poststr(request, "<tr><td>");
|
||||
hprintf255(request, "<form action=\"index\"><label for=\"select%i\">Channel %s Enum:</label>", i, CHANNEL_GetLabel(i));
|
||||
hprintf255(request, "<input type=\"hidden\" name=\"setIndex\" value=\"%i\">", i);
|
||||
hprintf255(request, "<select id=\"select%i\" name=\"set\" onchange=\"this.form.submit()\">", i);
|
||||
|
||||
bool found = false;
|
||||
for (int o = 0; o < en->numOptions; o++) {
|
||||
const char* selected;
|
||||
if (en->options[o].value == iValue) {
|
||||
selected = "selected";
|
||||
found = true;
|
||||
} else
|
||||
selected = "";
|
||||
hprintf255(request, "<option value=\"%i\" %s>%s [%i]</option>", en->options[o].value, selected, en->options[o].label,en->options[o].value);
|
||||
}
|
||||
if (!found) // create an item if no label is found
|
||||
hprintf255(request, "<option value=\"%i\" selected>undefined enum [%i]</option>", iValue,iValue);
|
||||
hprintf255(request, "</select></form>");
|
||||
poststr(request, "</td></tr>");
|
||||
}
|
||||
}
|
||||
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, "<tr><td>");
|
||||
hprintf255(request, "Channel %s = %s [%i]", CHANNEL_GetLabel(i), oLabel, iValue);
|
||||
poststr(request, "</td></tr>");
|
||||
}
|
||||
else if ((types = Channel_GetOptionsForChannelType(channelType, &numTypes)) != 0) {
|
||||
const char *what;
|
||||
@ -2236,6 +2287,54 @@ void doHomeAssistantDiscovery(const char* topic, http_request_t* request) {
|
||||
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:
|
||||
{
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
int numOptions;
|
||||
|
||||
@ -325,7 +325,7 @@ void http_html_start(http_request_t* request, const char* pagename) {
|
||||
}
|
||||
|
||||
|
||||
const char pageScriptPart1[] = "<script type='text/javascript'>var firstTime,lastTime,onlineFor,req=null,onlineForEl=null,getElement=e=>document.getElementById(e);function showState(){clearTimeout(firstTime),clearTimeout(lastTime),null!=req&&req.abort(),(e=getElement(\"state\"))&&((req=new XMLHttpRequest).onreadystatechange=()=>{4==req.readyState&&\"OK\"==req.statusText&&((\"INPUT\"!=document.activeElement.tagName||\"number\"!=document.activeElement.type&&\"color\"!=document.activeElement.type)&&(e.innerHTML=req.responseText),clearTimeout(firstTime),clearTimeout(lastTime),lastTime=setTimeout(showState,";
|
||||
const char pageScriptPart1[] = "<script type='text/javascript'>var firstTime,lastTime,onlineFor,req=null,onlineForEl=null,getElement=e=>document.getElementById(e);function showState(){clearTimeout(firstTime),clearTimeout(lastTime),null!=req&&req.abort(),(e=getElement(\"state\"))&&((req=new XMLHttpRequest).onreadystatechange=()=>{4==req.readyState&&\"OK\"==req.statusText&&(\"SELECT\"!=document.activeElement.tagName&&(\"INPUT\"!=document.activeElement.tagName||\"number\"!=document.activeElement.type&&\"color\"!=document.activeElement.type)&&(e.innerHTML=req.responseText),clearTimeout(firstTime),clearTimeout(lastTime),lastTime=setTimeout(showState,";
|
||||
const char pageScriptPart2[] = "))},req.open(\"GET\",\"index?state=1\",!0),req.send()),firstTime=setTimeout(showState,";
|
||||
const char pageScriptPart3[] = ")}function fmtUpTime(e){var t,n,o=Math.floor(e/86400);return e%=86400,t=Math.floor(e/3600),e%=3600,n=Math.floor(e/60),e=e%60,0<o?o+` days, ${t} hours, ${n} minutes and ${e} seconds`:0<t?t+` hours, ${n} minutes and ${e} seconds`:0<n?n+` minutes and ${e} seconds`:`just ${e} seconds`}function updateOnlineFor(){onlineForEl.textContent=fmtUpTime(++onlineFor)}function onLoad(){(onlineForEl=getElement(\"onlineFor\"))&&(onlineFor=parseInt(onlineForEl.dataset.initial,10))&&setInterval(updateOnlineFor,1e3),showState()}function submitTemperature(e){var t=getElement(\"form132\");getElement(\"kelvin132\").value=Math.round(1e6/parseInt(e.value)),t.submit()}window.addEventListener(\"load\",onLoad),history.pushState(null,\"\",window.location.pathname.slice(1)),setTimeout(()=>{var e=getElement(\"changed\");e&&(e.innerHTML=\"\")},5e3);</script>";
|
||||
|
||||
|
||||
@ -21,8 +21,9 @@ function showState() {
|
||||
if (req.readyState == 4 && req.statusText == "OK") {
|
||||
if (
|
||||
!(
|
||||
document.activeElement.tagName == "INPUT" &&
|
||||
(document.activeElement.type == "number" || document.activeElement.type == "color")
|
||||
document.activeElement.tagName == "SELECT" &&
|
||||
(document.activeElement.tagName == "INPUT" &&
|
||||
(document.activeElement.type == "number" || document.activeElement.type == "color"))
|
||||
)
|
||||
) {
|
||||
var stateEl = getElement("state");
|
||||
|
||||
@ -2392,6 +2392,8 @@ const char* g_channelTypeNames[] = {
|
||||
"Percent",
|
||||
"StopUpDown",
|
||||
"EnergyImport_kWh_div1000",
|
||||
"Enum",
|
||||
"ReadOnlyEnum",
|
||||
"error",
|
||||
"error",
|
||||
};
|
||||
|
||||
@ -1071,6 +1071,20 @@ typedef enum channelType_e {
|
||||
//chandetail:"file":"new_pins.h",
|
||||
//chandetail:"driver":""}
|
||||
ChType_EnergyImport_kWh_div1000,
|
||||
//chandetail:{"name":"Enum",
|
||||
//chandetail:"title":"Enum",
|
||||
//chandetail:"descr":"This channel type allows creating custom Enum types in combination with SetChannelEnum command. Ideal for defining TuyaMCU enum mappings.",
|
||||
//chandetail:"enum":"ChType_Enum",
|
||||
//chandetail:"file":"new_pins.h",
|
||||
//chandetail:"driver":""}
|
||||
ChType_Enum,
|
||||
//chandetail:{"name":"ReadOnlyEnum",
|
||||
//chandetail:"title":"ReadOnlyEnum",
|
||||
//chandetail:"descr":"Read Only Enum Channel type for use in combination with SetChannelEnum command. Ideal for defining TuyaMCU enum mappings.",
|
||||
//chandetail:"enum":"ChType_ReadOnlyEnum",
|
||||
//chandetail:"file":"new_pins.h",
|
||||
//chandetail:"driver":""}
|
||||
ChType_ReadOnlyEnum,
|
||||
//chandetail:{"name":"Max",
|
||||
//chandetail:"title":"TODO",
|
||||
//chandetail:"descr":"This is the current total number of available channel types.",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#ifdef WINDOWS
|
||||
|
||||
#include "selftest_local.h"
|
||||
#include "../cmnds/cmd_newEnums.h"
|
||||
#include "../cmnds/cmd_enums.h"
|
||||
|
||||
|
||||
void Test_Enums() {
|
||||
@ -12,8 +12,17 @@ void Test_Enums() {
|
||||
|
||||
SELFTEST_ASSERT(g_enums == 0);
|
||||
|
||||
CMD_ExecuteCommand("SetChannelEnum 4 1:Ok 0:Bad", 0);
|
||||
CMD_ExecuteCommand("SetChannelType 4 Enum", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 4 1:Bad 0:Ok", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 4 1:Ok 0:Bad", 0); // check options overwrite
|
||||
|
||||
// null checks
|
||||
SELFTEST_ASSERT(g_enums);
|
||||
SELFTEST_ASSERT(!strcmp(CMD_FindChannelEnumLabel(g_enums[14],1), "1"));
|
||||
CMD_GenEnumValueTemplate(g_enums[14], tmp, sizeof(tmp));
|
||||
// actual discovery template content assertions are in selftst_hass_discovery_ext.c
|
||||
SELFTEST_ASSERT(!strcmp(tmp,"{{ {99999:'Undefined'}[(value | int(99999))] | default(\"Undefined Enum [\"~value~\"]\") }}"));
|
||||
|
||||
for (int i = 0; i < CHANNEL_MAX; i++) {
|
||||
if (i != 4) {
|
||||
SELFTEST_ASSERT(!g_enums[i]);
|
||||
@ -27,9 +36,10 @@ void Test_Enums() {
|
||||
SELFTEST_ASSERT(g_enums[4]->options[1].value == 0);
|
||||
SELFTEST_ASSERT(!strcmp(g_enums[4]->options[1].label, "Bad"));
|
||||
|
||||
CMD_FormatEnumTemplate(g_enums[4], tmp, sizeof(tmp));
|
||||
CMD_FormatEnumTemplate(g_enums[4], tmp, sizeof(tmp),false);
|
||||
CMD_FormatEnumTemplate(g_enums[4], tmp, sizeof(tmp),true);
|
||||
|
||||
CMD_ExecuteCommand("SetChannelEnum 14 1:One 0:Zero 3:Three", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 14 \"1:One Switch\" 0:Zero 3:Three", 0);
|
||||
SELFTEST_ASSERT(g_enums);
|
||||
SELFTEST_ASSERT(g_enums[14]);
|
||||
SELFTEST_ASSERT(!g_enums[13]);
|
||||
@ -37,11 +47,17 @@ void Test_Enums() {
|
||||
|
||||
SELFTEST_ASSERT(g_enums[14]->numOptions == 3);
|
||||
SELFTEST_ASSERT(g_enums[14]->options[0].value == 1);
|
||||
SELFTEST_ASSERT(!strcmp(g_enums[14]->options[0].label, "One"));
|
||||
SELFTEST_ASSERT(!strcmp(g_enums[14]->options[0].label, "One Switch"));
|
||||
SELFTEST_ASSERT(g_enums[14]->options[1].value == 0);
|
||||
SELFTEST_ASSERT(!strcmp(g_enums[14]->options[1].label, "Zero"));
|
||||
SELFTEST_ASSERT(g_enums[14]->options[2].value == 3);
|
||||
SELFTEST_ASSERT(!strcmp(g_enums[14]->options[2].label, "Three"));
|
||||
|
||||
SELFTEST_ASSERT(!strcmp(CMD_FindChannelEnumLabel(g_enums[14],1), "One Switch"));
|
||||
SELFTEST_ASSERT(!strcmp(CMD_FindChannelEnumLabel(g_enums[14],0), "Zero"));
|
||||
SELFTEST_ASSERT(!strcmp(CMD_FindChannelEnumLabel(g_enums[14],3), "Three"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -681,6 +681,107 @@ void Test_HassDiscovery_Channel_Toggle_2x() {
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant", true, 0, 0, "stat_t", "~/5/get");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant", true, 0, 0, "cmd_t", "~/5/set");
|
||||
}
|
||||
void Test_HassDiscovery_Enum() {
|
||||
const char *shortName = "Enumtest";
|
||||
const char *fullName = "Windows Fake Enum";
|
||||
const char *mqttName = "testEnum";
|
||||
|
||||
SIM_ClearOBK(shortName);
|
||||
SIM_ClearAndPrepareForMQTTTesting(mqttName, "bekens");
|
||||
|
||||
CFG_SetShortDeviceName(shortName);
|
||||
CFG_SetDeviceName(fullName);
|
||||
|
||||
SIM_ClearMQTTHistory();
|
||||
|
||||
CMD_ExecuteCommand("SetChannelType 4 Enum", 0);
|
||||
CMD_ExecuteCommand("SetChannelLabel 4 EnumFour", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 4 1:Ok 0:Bad", 0);
|
||||
|
||||
CMD_ExecuteCommand("SetChannelType 14 Enum", 0);
|
||||
CMD_ExecuteCommand("SetChannelLabel 14 Enum14", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 14 \"1:One Switch\" 0:Zero 3:Three", 0);
|
||||
|
||||
|
||||
CMD_ExecuteCommand("scheduleHADiscovery 1", 0);
|
||||
Sim_RunSeconds(5, false);
|
||||
|
||||
|
||||
// OBK device should publish JSON on MQTT topic "homeassistant/select"
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("homeassistant/select", true);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "name", shortName);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "sw", USER_SW_VER);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "mf", MANUFACTURER);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "mdl", PLATFORM_MCU_NAME);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING(0, "~", mqttName);
|
||||
|
||||
// channel 4
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_3KEY("homeassistant/select", true, 0, 0,
|
||||
"unique_id", "EnumFour",
|
||||
"state_topic", "~/4/get",
|
||||
"command_topic", "~/4/set");
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING_NESTED_ARRAY(0,"options",0,"Ok");
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING_NESTED_ARRAY(0,"options",1,"Bad");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/select", true, 0, 0, "command_template", "{{ {'Ok':'1', 'Bad':'0', 'Undefined':'99999'}[value] | default(99999) }}");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/select", true, 0, 0, "value_template", "{{ {1:'Ok', 0:'Bad', 99999:'Undefined'}[(value | int(99999))] | default(\"Undefined Enum [\"~value~\"]\") }}");
|
||||
|
||||
// channel 14
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_3KEY("homeassistant/select", true, 0, 0,
|
||||
"unique_id", "Enum14",
|
||||
"state_topic", "~/14/get",
|
||||
"command_topic", "~/14/set");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/select", true, 0, 0, "value_template", "{{ {1:'One Switch', 0:'Zero', 3:'Three', 99999:'Undefined'}[(value | int(99999))] | default(\"Undefined Enum [\"~value~\"]\") }}");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/select", true, 0, 0, "command_template", "{{ {'One Switch':'1', 'Zero':'0', 'Three':'3', 'Undefined':'99999'}[value] | default(99999) }}");
|
||||
}
|
||||
|
||||
void Test_HassDiscovery_ReadOnlyEnum() {
|
||||
const char *shortName = "ReadOnlyEnumtest";
|
||||
const char *fullName = "Windows Fake ReadOnlyEnum";
|
||||
const char *mqttName = "testReadOnlyEnum";
|
||||
|
||||
SIM_ClearOBK(shortName);
|
||||
SIM_ClearAndPrepareForMQTTTesting(mqttName, "bekens");
|
||||
|
||||
CFG_SetShortDeviceName(shortName);
|
||||
CFG_SetDeviceName(fullName);
|
||||
|
||||
SIM_ClearMQTTHistory();
|
||||
|
||||
CMD_ExecuteCommand("SetChannelType 4 ReadOnlyEnum", 0);
|
||||
CMD_ExecuteCommand("SetChannelLabel 4 ReadOnlyEnumFour", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 4 1:Ok 0:Bad", 0);
|
||||
|
||||
//CHANNEL_SetType(14, ChType_ReadOnlyEnum);
|
||||
CMD_ExecuteCommand("SetChannelType 14 ReadOnlyEnum", 0);
|
||||
CMD_ExecuteCommand("SetChannelLabel 14 ReadOnlyEnum14", 0);
|
||||
CMD_ExecuteCommand("SetChannelEnum 14 \"1:One Switch\" 0:Zero 3:Three", 0);
|
||||
|
||||
CMD_ExecuteCommand("scheduleHADiscovery 1", 0);
|
||||
Sim_RunSeconds(5, false);
|
||||
|
||||
// MQTT Config should be sensor for ReadOnlyEnum
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT("homeassistant/sensor", true);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "name", shortName);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "sw", USER_SW_VER);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "mf", MANUFACTURER);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING("dev", "mdl", PLATFORM_MCU_NAME);
|
||||
SELFTEST_ASSERT_JSON_VALUE_STRING(0, "~", mqttName);
|
||||
|
||||
// channel 4
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/sensor", true, 0, 0, "name", "ReadOnlyEnumFour");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/sensor", true, 0, 0, "stat_t", "~/4/get");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/sensor", true, 0, 0, "value_template", "{{ {1:'Ok', 0:'Bad', 99999:'Undefined'}[(value | int(99999))] | default(\"Undefined Enum [\"~value~\"]\") }}");
|
||||
// state_class should not be defined
|
||||
SELFTEST_ASSERT_HAS_NOT_MQTT_JSON_SENT_ANY("homeassistant/sensor/Windows_Fake_ReadOnlyEnum_sensor_4", true, 0, 0, "stat_cla", "temperature");
|
||||
SELFTEST_ASSERT_HAS_NOT_MQTT_JSON_SENT_ANY("homeassistant/sensor/Windows_Fake_ReadOnlyEnum_sensor_4", true, 0, 0, "stat_cla", "measurement");
|
||||
|
||||
// channel 14
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/sensor", true, 0, 0, "name", "ReadOnlyEnum14");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/sensor", true, 0, 0, "stat_t", "~/14/get");
|
||||
SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY("homeassistant/sensor", true, 0, 0, "value_template", "{{ {1:'One Switch', 0:'Zero', 3:'Three', 99999:'Undefined'}[(value | int(99999))] | default(\"Undefined Enum [\"~value~\"]\") }}");
|
||||
|
||||
}
|
||||
|
||||
void Test_HassDiscovery_Ext() {
|
||||
Test_HassDiscovery_TuyaMCU_VoltageCurrentPower();
|
||||
Test_HassDiscovery_TuyaMCU_Power10();
|
||||
@ -705,6 +806,8 @@ void Test_HassDiscovery_Ext() {
|
||||
Test_HassDiscovery_Channel_Smoke();
|
||||
Test_HassDiscovery_Channel_Ph();
|
||||
|
||||
Test_HassDiscovery_Enum();
|
||||
Test_HassDiscovery_ReadOnlyEnum();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -250,6 +250,19 @@ const char *Test_GetJSONValue_StrFromArray(int index, const char *key) {
|
||||
return "";
|
||||
return tmp->valuestring;
|
||||
}
|
||||
const char *Test_GetJSONValue_StrFromNestedArray(const char *par, const char *key, int index) {
|
||||
cJSON *tmp;
|
||||
cJSON *parent;
|
||||
|
||||
parent = Test_GetJSONValue_Generic(key, par);
|
||||
if (parent == 0)
|
||||
return "";
|
||||
tmp = cJSON_GetArrayItem(parent, index);
|
||||
if (tmp == 0)
|
||||
return "";
|
||||
printf("Test_GetJSONValue_StrFromNestedArray DEBUG will return %s for %s[%i]\n", tmp->valuestring, key, index);
|
||||
return tmp->valuestring;
|
||||
}
|
||||
|
||||
const char *Test_GetJSONValue_String(const char *keyword, const char *obj) {
|
||||
cJSON *tmp;
|
||||
|
||||
@ -38,6 +38,7 @@ void SelfTest_Failed(const char *file, const char *function, int line, const cha
|
||||
#define SELFTEST_ASSERT_JSON_VALUE_STRING_NESTED2(par1, par2, varName, res) SELFTEST_ASSERT((!strcmp(Test_GetJSONValue_String_Nested2(par1, par2,varName),res)));
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_INT(index, key, valInt) SELFTEST_ASSERT((Test_GetJSONValue_IntFromArray(index,key)==valInt));
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_ARRAY_ITEM_STR(index, key, valInt) SELFTEST_ASSERT((!strcmp(Test_GetJSONValue_StrFromArray(index,key),valInt)));
|
||||
#define SELFTEST_ASSERT_JSON_VALUE_STRING_NESTED_ARRAY(par, varName, index, res) SELFTEST_ASSERT((!strcmp(Test_GetJSONValue_StrFromNestedArray(par, varName, index),res)));
|
||||
#define SELFTEST_ASSERT_HTTP_HAS_LED_DIMMER(bHas) SELFTEST_ASSERT((bHas) == SIM_HasHTTPDimmer());
|
||||
#define SELFTEST_ASSERT_HTTP_HAS_LED_TEMPERATURE(bHas) SELFTEST_ASSERT((bHas) == SIM_HasHTTPTemperature());
|
||||
#define SELFTEST_ASSERT_HTTP_HAS_LED_RGB(bHas) SELFTEST_ASSERT((bHas) == SIM_HasHTTPRGB());
|
||||
@ -62,6 +63,7 @@ void SelfTest_Failed(const char *file, const char *function, int line, const cha
|
||||
#define SELFTEST_ASSERT_FLAG(flag, value) SELFTEST_ASSERT(CFG_HasFlag(flag)==value);
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_JSON_SENT(topic, bPrefixMode) SELFTEST_ASSERT(!SIM_BeginParsingMQTTJSON(topic, bPrefixMode));
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY(topic, bPrefixMode, object1, object2, key, value) SELFTEST_ASSERT(SIM_HasMQTTHistoryStringWithJSONPayload(topic, bPrefixMode, object1, object2, key, value, 0, 0, 0, 0, 0, 0));
|
||||
#define SELFTEST_ASSERT_HAS_NOT_MQTT_JSON_SENT_ANY(topic, bPrefixMode, object1, object2, key, value) SELFTEST_ASSERT(!SIM_HasMQTTHistoryStringWithJSONPayload(topic, bPrefixMode, object1, object2, key, value, 0, 0, 0, 0, 0, 0));
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_TWOKEY(topic, bPrefixMode, object1, object2, key, value, key2, value2) SELFTEST_ASSERT(SIM_HasMQTTHistoryStringWithJSONPayload(topic, bPrefixMode, object1, object2, key, value, key2, value2, 0, 0, 0, 0));
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_3KEY(topic, bPrefixMode, object1, object2, key, value, key2, value2, key3, value3) SELFTEST_ASSERT(SIM_HasMQTTHistoryStringWithJSONPayload(topic, bPrefixMode, object1, object2, key, value, key2, value2, key3, value3, 0, 0));
|
||||
#define SELFTEST_ASSERT_HAS_MQTT_JSON_SENT_ANY_4KEY(topic, bPrefixMode, object1, object2, key, value, key2, value2, key3, value3, key4, value4) SELFTEST_ASSERT(SIM_HasMQTTHistoryStringWithJSONPayload(topic, bPrefixMode, object1, object2, key, value, key2, value2, key3, value3, key4, value4));
|
||||
@ -177,6 +179,7 @@ const char *Test_GetJSONValue_String_Nested(const char *par1, const char *keywor
|
||||
const char *Test_GetJSONValue_String_Nested2(const char *par1, const char *par2, const char *keyword);
|
||||
int Test_GetJSONValue_IntFromArray(int index, const char *obj);
|
||||
const char *Test_GetJSONValue_StrFromArray(int index, const char *obj);
|
||||
const char *Test_GetJSONValue_StrFromNestedArray(const char *par, const char *key, int index);
|
||||
|
||||
void SIM_SendFakeMQTT(const char *text, const char *arguments);
|
||||
void SIM_SendFakeMQTTAndRunSimFrame_CMND(const char *command, const char *arguments);
|
||||
|
||||
@ -193,7 +193,7 @@ void Win_DoUnitTests() {
|
||||
Test_Demo_ConditionalRelay();
|
||||
Test_Expressions_RunTests_Braces();
|
||||
Test_Expressions_RunTests_Basic();
|
||||
//Test_Enums();
|
||||
Test_Enums();
|
||||
Test_Backlog();
|
||||
Test_DoorSensor();
|
||||
Test_LEDstrips();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user