Merge pull request #2630 from proddy/dev

API changes
This commit is contained in:
Proddy
2025-10-06 23:23:54 +02:00
committed by GitHub
45 changed files with 8228 additions and 8101 deletions

View File

@@ -28,7 +28,4 @@ jobs:
uses: SonarSource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
--define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json"

View File

@@ -4446,7 +4446,7 @@
| hc1.seltemp | selected room temperature | int16 (>=0<=30) | C | true | HC | 0 | 1 | 1/2 |
| hc1.currtemp | current room temperature | int16 (>=-3199<=3199) | C | false | HC | 1 | 1 | 1/10 |
| hc1.haclimate | mqtt discovery current room temperature | enum [selTemp\|roomTemp] (>=5<=30) | | false | HC | 2 | 1 | 1 |
| hc1.mode | operating mode | enum [manual\|auto] | | false | HC | 3 | 1 | 1 |
| hc1.mode | operating mode | enum [off\|manual] | | true | HC | 3 | 1 | 1 |
| hc1.targetflowtemp | target flow temperature | uint8 (>=0<=254) | C | false | HC | 18 | 1 | 1 |
| hc1.heatingtype | heating type | enum [off\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |
@@ -4680,6 +4680,8 @@
| errorcode | error code | string | | false | DEVICE_DATA | 0 | 8 | 1 |
| lastcode | last error code | string | | false | DEVICE_DATA | 8 | 25 | 1 |
| datetime | date/time | string | | true | DEVICE_DATA | 33 | 13 | 1 |
| display | display | enum [dhw temperature\|date\|external temperature] | | true | DEVICE_DATA | 65 | 1 | 1 |
| language | language | enum [german\|italian\|french\|dutch] | | true | DEVICE_DATA | 66 | 1 | 1 |
| hybridstrategy | hybrid control strategy | enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix] | | true | DEVICE_DATA | 54 | 1 | 1 |
| switchovertemp | outside switchover temperature | int8 (>=-20<=20) | C | true | DEVICE_DATA | 55 | 1 | 1 |
| energycostratio | energy cost ratio | uint8 (>=0<=20) | | true | DEVICE_DATA | 56 | 1 | 1/10 |
@@ -4719,6 +4721,8 @@
| errorcode | error code | string | | false | DEVICE_DATA | 0 | 8 | 1 |
| lastcode | last error code | string | | false | DEVICE_DATA | 8 | 25 | 1 |
| datetime | date/time | string | | true | DEVICE_DATA | 33 | 13 | 1 |
| display | display | enum [dhw temperature\|date\|external temperature] | | true | DEVICE_DATA | 65 | 1 | 1 |
| language | language | enum [german\|italian\|french\|dutch] | | true | DEVICE_DATA | 66 | 1 | 1 |
| hybridstrategy | hybrid control strategy | enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix] | | true | DEVICE_DATA | 54 | 1 | 1 |
| switchovertemp | outside switchover temperature | int8 (>=-20<=20) | C | true | DEVICE_DATA | 55 | 1 | 1 |
| energycostratio | energy cost ratio | uint8 (>=0<=20) | | true | DEVICE_DATA | 56 | 1 | 1/10 |
@@ -4953,6 +4957,8 @@
| errorcode | error code | string | | false | DEVICE_DATA | 0 | 8 | 1 |
| lastcode | last error code | string | | false | DEVICE_DATA | 8 | 25 | 1 |
| datetime | date/time | string | | true | DEVICE_DATA | 33 | 13 | 1 |
| display | display | enum [dhw temperature\|date\|external temperature] | | true | DEVICE_DATA | 65 | 1 | 1 |
| language | language | enum [german\|italian\|french\|dutch] | | true | DEVICE_DATA | 66 | 1 | 1 |
| hybridstrategy | hybrid control strategy | enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix] | | true | DEVICE_DATA | 54 | 1 | 1 |
| switchovertemp | outside switchover temperature | int8 (>=-20<=20) | C | true | DEVICE_DATA | 55 | 1 | 1 |
| energycostratio | energy cost ratio | uint8 (>=0<=20) | | true | DEVICE_DATA | 56 | 1 | 1/10 |
@@ -4992,6 +4998,8 @@
| errorcode | error code | string | | false | DEVICE_DATA | 0 | 8 | 1 |
| lastcode | last error code | string | | false | DEVICE_DATA | 8 | 25 | 1 |
| datetime | date/time | string | | true | DEVICE_DATA | 33 | 13 | 1 |
| display | display | enum [dhw temperature\|date\|external temperature] | | true | DEVICE_DATA | 65 | 1 | 1 |
| language | language | enum [german\|italian\|french\|dutch] | | true | DEVICE_DATA | 66 | 1 | 1 |
| hybridstrategy | hybrid control strategy | enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix] | | true | DEVICE_DATA | 54 | 1 | 1 |
| switchovertemp | outside switchover temperature | int8 (>=-20<=20) | C | true | DEVICE_DATA | 55 | 1 | 1 |
| energycostratio | energy cost ratio | uint8 (>=0<=20) | | true | DEVICE_DATA | 56 | 1 | 1/10 |
@@ -5031,6 +5039,8 @@
| errorcode | error code | string | | false | DEVICE_DATA | 0 | 8 | 1 |
| lastcode | last error code | string | | false | DEVICE_DATA | 8 | 25 | 1 |
| datetime | date/time | string | | true | DEVICE_DATA | 33 | 13 | 1 |
| display | display | enum [dhw temperature\|date\|external temperature] | | true | DEVICE_DATA | 65 | 1 | 1 |
| language | language | enum [german\|italian\|french\|dutch] | | true | DEVICE_DATA | 66 | 1 | 1 |
| hybridstrategy | hybrid control strategy | enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix] | | true | DEVICE_DATA | 54 | 1 | 1 |
| switchovertemp | outside switchover temperature | int8 (>=-20<=20) | C | true | DEVICE_DATA | 55 | 1 | 1 |
| energycostratio | energy cost ratio | uint8 (>=0<=20) | | true | DEVICE_DATA | 56 | 1 | 1/10 |
@@ -5070,6 +5080,8 @@
| errorcode | error code | string | | false | DEVICE_DATA | 0 | 8 | 1 |
| lastcode | last error code | string | | false | DEVICE_DATA | 8 | 25 | 1 |
| datetime | date/time | string | | true | DEVICE_DATA | 33 | 13 | 1 |
| display | display | enum [dhw temperature\|date\|external temperature] | | true | DEVICE_DATA | 65 | 1 | 1 |
| language | language | enum [german\|italian\|french\|dutch] | | true | DEVICE_DATA | 66 | 1 | 1 |
| hybridstrategy | hybrid control strategy | enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix] | | true | DEVICE_DATA | 54 | 1 | 1 |
| switchovertemp | outside switchover temperature | int8 (>=-20<=20) | C | true | DEVICE_DATA | 55 | 1 | 1 |
| energycostratio | energy cost ratio | uint8 (>=0<=20) | | true | DEVICE_DATA | 56 | 1 | 1/10 |
@@ -5243,7 +5255,7 @@
| hc1.seltemp | selected room temperature | int16 (>=0<=30) | C | true | HC | 0 | 1 | 1/2 |
| hc1.currtemp | current room temperature | int16 (>=-3199<=3199) | C | false | HC | 1 | 1 | 1/10 |
| hc1.haclimate | mqtt discovery current room temperature | enum [selTemp\|roomTemp] (>=5<=30) | | false | HC | 2 | 1 | 1 |
| hc1.mode | operating mode | enum [manual\|auto] | | false | HC | 3 | 1 | 1 |
| hc1.mode | operating mode | enum [off\|manual] | | true | HC | 3 | 1 | 1 |
| hc1.targetflowtemp | target flow temperature | uint8 (>=0<=254) | C | false | HC | 18 | 1 | 1 |
| hc1.heatingtype | heating type | enum [off\|radiator\|convector\|floor] | | true | HC | 19 | 1 | 1 |

View File

@@ -3815,7 +3815,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"CR11",thermostat,10,hc1.seltemp,selected room temperature,int16 (>=0<=30),C,true,number.thermostat_hc1_selected_room_temperature,number.thermostat_hc1_seltemp,6,1,1/2,0,1
"CR11",thermostat,10,hc1.currtemp,current room temperature,int16 (>=-3199<=3199),C,false,sensor.thermostat_hc1_current_room_temperature,sensor.thermostat_hc1_currtemp,6,1,1/10,1,1
"CR11",thermostat,10,hc1.haclimate,mqtt discovery current room temperature,enum [selTemp\|roomTemp] (>=5<=30), ,false,sensor.thermostat_hc1_mqtt_discovery_current_room_temperature,sensor.thermostat_hc1_haclimate,6,1,1,2,1
"CR11",thermostat,10,hc1.mode,operating mode,enum [manual\|auto], ,false,sensor.thermostat_hc1_operating_mode,sensor.thermostat_hc1_mode,6,1,1,3,1
"CR11",thermostat,10,hc1.mode,operating mode,enum [off\|manual], ,true,select.thermostat_hc1_operating_mode,select.thermostat_hc1_mode,6,1,1,3,1
"CR11",thermostat,10,hc1.targetflowtemp,target flow temperature,uint8 (>=0<=254),C,false,sensor.thermostat_hc1_target_flow_temperature,sensor.thermostat_hc1_targetflowtemp,6,1,1,18,1
"CR11",thermostat,10,hc1.heatingtype,heating type,enum [off\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"RC10",thermostat,65,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
@@ -4287,7 +4287,7 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.seltemp,selected room temperature,int16 (>=0<=30),C,true,number.thermostat_hc1_selected_room_temperature,number.thermostat_hc1_seltemp,6,1,1/2,0,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.currtemp,current room temperature,int16 (>=-3199<=3199),C,false,sensor.thermostat_hc1_current_room_temperature,sensor.thermostat_hc1_currtemp,6,1,1/10,1,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.haclimate,mqtt discovery current room temperature,enum [selTemp\|roomTemp] (>=5<=30), ,false,sensor.thermostat_hc1_mqtt_discovery_current_room_temperature,sensor.thermostat_hc1_haclimate,6,1,1,2,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.mode,operating mode,enum [manual\|auto], ,false,sensor.thermostat_hc1_operating_mode,sensor.thermostat_hc1_mode,6,1,1,3,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.mode,operating mode,enum [off\|manual], ,true,select.thermostat_hc1_operating_mode,select.thermostat_hc1_mode,6,1,1,3,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.targetflowtemp,target flow temperature,uint8 (>=0<=254),C,false,sensor.thermostat_hc1_target_flow_temperature,sensor.thermostat_hc1_targetflowtemp,6,1,1,18,1
"RC100, CR10, Moduline 1000/1010",thermostat,165,hc1.heatingtype,heating type,enum [off\|radiator\|convector\|floor], ,true,select.thermostat_hc1_heating_type,select.thermostat_hc1_heatingtype,6,1,1,19,1
"Rego 2000/3000",thermostat,172,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
@@ -4664,6 +4664,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW100",thermostat,105,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW100",thermostat,105,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW100",thermostat,105,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
"FW100",thermostat,105,display,display,enum [dhw temperature\|date\|external temperature], ,true,select.thermostat_display,select.thermostat_display,6,0,1,65,1
"FW100",thermostat,105,language,language,enum [german\|italian\|french\|dutch], ,true,select.thermostat_language,select.thermostat_language,6,0,1,66,1
"FW100",thermostat,105,hybridstrategy,hybrid control strategy,enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix], ,true,select.thermostat_hybrid_control_strategy,select.thermostat_hybridstrategy,6,0,1,54,1
"FW100",thermostat,105,switchovertemp,outside switchover temperature,int8 (>=-20<=20),C,true,number.thermostat_outside_switchover_temperature,number.thermostat_switchovertemp,6,0,1,55,1
"FW100",thermostat,105,energycostratio,energy cost ratio,uint8 (>=0<=20), ,true,number.thermostat_energy_cost_ratio,number.thermostat_energycostratio,6,0,1/10,56,1
@@ -4699,6 +4701,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW200",thermostat,106,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW200",thermostat,106,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW200",thermostat,106,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
"FW200",thermostat,106,display,display,enum [dhw temperature\|date\|external temperature], ,true,select.thermostat_display,select.thermostat_display,6,0,1,65,1
"FW200",thermostat,106,language,language,enum [german\|italian\|french\|dutch], ,true,select.thermostat_language,select.thermostat_language,6,0,1,66,1
"FW200",thermostat,106,hybridstrategy,hybrid control strategy,enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix], ,true,select.thermostat_hybrid_control_strategy,select.thermostat_hybridstrategy,6,0,1,54,1
"FW200",thermostat,106,switchovertemp,outside switchover temperature,int8 (>=-20<=20),C,true,number.thermostat_outside_switchover_temperature,number.thermostat_switchovertemp,6,0,1,55,1
"FW200",thermostat,106,energycostratio,energy cost ratio,uint8 (>=0<=20), ,true,number.thermostat_energy_cost_ratio,number.thermostat_energycostratio,6,0,1/10,56,1
@@ -4804,6 +4808,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FB10",thermostat,109,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FB10",thermostat,109,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FB10",thermostat,109,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
"FB10",thermostat,109,display,display,enum [dhw temperature\|date\|external temperature], ,true,select.thermostat_display,select.thermostat_display,6,0,1,65,1
"FB10",thermostat,109,language,language,enum [german\|italian\|french\|dutch], ,true,select.thermostat_language,select.thermostat_language,6,0,1,66,1
"FB10",thermostat,109,hybridstrategy,hybrid control strategy,enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix], ,true,select.thermostat_hybrid_control_strategy,select.thermostat_hybridstrategy,6,0,1,54,1
"FB10",thermostat,109,switchovertemp,outside switchover temperature,int8 (>=-20<=20),C,true,number.thermostat_outside_switchover_temperature,number.thermostat_switchovertemp,6,0,1,55,1
"FB10",thermostat,109,energycostratio,energy cost ratio,uint8 (>=0<=20), ,true,number.thermostat_energy_cost_ratio,number.thermostat_energycostratio,6,0,1/10,56,1
@@ -4839,6 +4845,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FB100",thermostat,110,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FB100",thermostat,110,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FB100",thermostat,110,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
"FB100",thermostat,110,display,display,enum [dhw temperature\|date\|external temperature], ,true,select.thermostat_display,select.thermostat_display,6,0,1,65,1
"FB100",thermostat,110,language,language,enum [german\|italian\|french\|dutch], ,true,select.thermostat_language,select.thermostat_language,6,0,1,66,1
"FB100",thermostat,110,hybridstrategy,hybrid control strategy,enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix], ,true,select.thermostat_hybrid_control_strategy,select.thermostat_hybridstrategy,6,0,1,54,1
"FB100",thermostat,110,switchovertemp,outside switchover temperature,int8 (>=-20<=20),C,true,number.thermostat_outside_switchover_temperature,number.thermostat_switchovertemp,6,0,1,55,1
"FB100",thermostat,110,energycostratio,energy cost ratio,uint8 (>=0<=20), ,true,number.thermostat_energy_cost_ratio,number.thermostat_energycostratio,6,0,1/10,56,1
@@ -4909,6 +4917,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW500",thermostat,116,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW500",thermostat,116,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW500",thermostat,116,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
"FW500",thermostat,116,display,display,enum [dhw temperature\|date\|external temperature], ,true,select.thermostat_display,select.thermostat_display,6,0,1,65,1
"FW500",thermostat,116,language,language,enum [german\|italian\|french\|dutch], ,true,select.thermostat_language,select.thermostat_language,6,0,1,66,1
"FW500",thermostat,116,hybridstrategy,hybrid control strategy,enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix], ,true,select.thermostat_hybrid_control_strategy,select.thermostat_hybridstrategy,6,0,1,54,1
"FW500",thermostat,116,switchovertemp,outside switchover temperature,int8 (>=-20<=20),C,true,number.thermostat_outside_switchover_temperature,number.thermostat_switchovertemp,6,0,1,55,1
"FW500",thermostat,116,energycostratio,energy cost ratio,uint8 (>=0<=20), ,true,number.thermostat_energy_cost_ratio,number.thermostat_energycostratio,6,0,1/10,56,1
@@ -5014,6 +5024,8 @@ device name,device type,product id,shortname,fullname,type [options...] \| (min/
"FW120",thermostat,192,errorcode,error code,string, ,false,sensor.thermostat_error_code,sensor.thermostat_errorcode,6,0,1,0,8
"FW120",thermostat,192,lastcode,last error code,string, ,false,sensor.thermostat_last_error_code,sensor.thermostat_lastcode,6,0,1,8,25
"FW120",thermostat,192,datetime,date/time,string, ,true,sensor.thermostat_date/time,sensor.thermostat_datetime,6,0,1,33,13
"FW120",thermostat,192,display,display,enum [dhw temperature\|date\|external temperature], ,true,select.thermostat_display,select.thermostat_display,6,0,1,65,1
"FW120",thermostat,192,language,language,enum [german\|italian\|french\|dutch], ,true,select.thermostat_language,select.thermostat_language,6,0,1,66,1
"FW120",thermostat,192,hybridstrategy,hybrid control strategy,enum [co2 optimized\|cost optimized\|outside temp switched\|co2 cost mix], ,true,select.thermostat_hybrid_control_strategy,select.thermostat_hybridstrategy,6,0,1,54,1
"FW120",thermostat,192,switchovertemp,outside switchover temperature,int8 (>=-20<=20),C,true,number.thermostat_outside_switchover_temperature,number.thermostat_switchovertemp,6,0,1,55,1
"FW120",thermostat,192,energycostratio,energy cost ratio,uint8 (>=0<=20), ,true,number.thermostat_energy_cost_ratio,number.thermostat_energycostratio,6,0,1/10,56,1
Can't render this file because it is too large.

View File

@@ -13,7 +13,7 @@ telegram_type_id,name,is_fetched
0x19,UBAMonitorSlow,
0x1A,UBASetPoints,
0x1C,UBAMaintenanceStatus,
0x1E,HydrTemp,
0x1E,WM10TempMessage,
0x23,JunkersSetMixer,fetched
0x27,UBASettingsWW,fetched
0x28,WeatherComp,fetched
@@ -76,7 +76,7 @@ telegram_type_id,name,is_fetched
0x0103,ISM1StatusMessage,fetched
0x0104,ISM2StatusMessage,
0x010C,IPMStatusMessage,
0x011E,IPMTempMessage,
0x011E,JunkersDisp,fetched
0x012E,HPEnergy1,
0x013B,HPEnergy2,
0x0165,JunkersSet,
@@ -111,8 +111,8 @@ telegram_type_id,name,is_fetched
0x02A0,RC300Curves,
0x02A1,RC300Curves,
0x02A2,RC300Curves,
0x02A5,RC300Monitor,fetched
0x02A6,RC300Monitor,
0x02A5,EasyMonitor,
0x02A6,CRFMonitor,
0x02A7,RC300Monitor,
0x02A8,CRFMonitor,
0x02A9,RC300Monitor,
@@ -135,7 +135,7 @@ telegram_type_id,name,is_fetched
0x02BE,RC300Set,
0x02BF,RC300Set,
0x02C0,RC300Set,
0x02CC,RC300Set2,
0x02CC,HPPressure,fetched
0x02CD,MMPLUSConfigMessage,fetched
0x02CE,RC300Set2,
0x02D0,RC300Set2,
1 telegram_type_id name is_fetched
13 0x19 UBAMonitorSlow
14 0x1A UBASetPoints
15 0x1C UBAMaintenanceStatus
16 0x1E HydrTemp WM10TempMessage
17 0x23 JunkersSetMixer fetched
18 0x27 UBASettingsWW fetched
19 0x28 WeatherComp fetched
76 0x0103 ISM1StatusMessage fetched
77 0x0104 ISM2StatusMessage
78 0x010C IPMStatusMessage
79 0x011E IPMTempMessage JunkersDisp fetched
80 0x012E HPEnergy1
81 0x013B HPEnergy2
82 0x0165 JunkersSet
111 0x02A0 RC300Curves
112 0x02A1 RC300Curves
113 0x02A2 RC300Curves
114 0x02A5 RC300Monitor EasyMonitor fetched
115 0x02A6 RC300Monitor CRFMonitor
116 0x02A7 RC300Monitor
117 0x02A8 CRFMonitor
118 0x02A9 RC300Monitor
135 0x02BE RC300Set
136 0x02BF RC300Set
137 0x02C0 RC300Set
138 0x02CC RC300Set2 HPPressure fetched
139 0x02CD MMPLUSConfigMessage fetched
140 0x02CE RC300Set2
141 0x02D0 RC300Set2

View File

@@ -25,43 +25,43 @@
"@alova/adapter-xhr": "2.2.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.1",
"@mui/material": "^7.3.1",
"@mui/icons-material": "^7.3.4",
"@mui/material": "^7.3.4",
"@table-library/react-table-library": "4.1.15",
"alova": "3.3.4",
"async-validator": "^4.2.5",
"formidable": "^3.5.4",
"jwt-decode": "^4.0.0",
"magic-string": "^0.30.18",
"magic-string": "^0.30.19",
"mime-types": "^3.0.1",
"preact": "^10.27.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"preact": "^10.27.2",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-icons": "^5.5.0",
"react-router": "^7.8.2",
"react-router": "^7.9.3",
"react-toastify": "^11.0.5",
"typesafe-i18n": "^5.26.2",
"typescript": "^5.9.2"
"typescript": "^5.9.3"
},
"devDependencies": {
"@babel/core": "^7.28.3",
"@eslint/js": "^9.34.0",
"@babel/core": "^7.28.4",
"@eslint/js": "^9.37.0",
"@preact/compat": "^18.3.1",
"@preact/preset-vite": "^2.10.2",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/node": "^24.3.0",
"@types/react": "^19.1.12",
"@types/react-dom": "^19.1.8",
"@types/node": "^24.7.0",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"concurrently": "^9.2.1",
"eslint": "^9.34.0",
"eslint": "^9.37.0",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"rollup-plugin-visualizer": "^6.0.3",
"terser": "^5.43.1",
"typescript-eslint": "^8.41.0",
"vite": "^7.1.3",
"rollup-plugin-visualizer": "^6.0.4",
"terser": "^5.44.0",
"typescript-eslint": "^8.46.0",
"vite": "^7.1.9",
"vite-plugin-imagemin": "^0.6.1",
"vite-tsconfig-paths": "^5.1.4"
},
"packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34"
}

1382
interface/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -15,5 +15,5 @@
"itty-router": "^5.0.22",
"prettier": "^3.6.2"
},
"packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34"
}

View File

@@ -46,8 +46,8 @@ packages:
resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.28.3':
resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==}
'@babel/parser@7.28.4':
resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
engines: {node: '>=6.0.0'}
hasBin: true
@@ -55,12 +55,12 @@ packages:
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.28.3':
resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==}
'@babel/traverse@7.28.4':
resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
engines: {node: '>=6.9.0'}
'@babel/types@7.28.2':
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
'@babel/types@7.28.4':
resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
engines: {node: '>=6.9.0'}
'@jridgewell/gen-mapping@0.3.13':
@@ -73,8 +73,8 @@ packages:
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@jridgewell/trace-mapping@0.3.30':
resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==}
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@msgpack/msgpack@3.1.2':
resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==}
@@ -106,8 +106,8 @@ packages:
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
@@ -166,10 +166,10 @@ snapshots:
'@babel/generator@7.28.3':
dependencies:
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.30
'@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0
'@babel/helper-globals@7.28.0': {}
@@ -178,29 +178,29 @@ snapshots:
'@babel/helper-validator-identifier@7.27.1': {}
'@babel/parser@7.28.3':
'@babel/parser@7.28.4':
dependencies:
'@babel/types': 7.28.2
'@babel/types': 7.28.4
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/parser': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@babel/traverse@7.28.3':
'@babel/traverse@7.28.4':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.3
'@babel/helper-globals': 7.28.0
'@babel/parser': 7.28.3
'@babel/parser': 7.28.4
'@babel/template': 7.27.2
'@babel/types': 7.28.2
debug: 4.4.1
'@babel/types': 7.28.4
debug: 4.4.3
transitivePeerDependencies:
- supports-color
'@babel/types@7.28.2':
'@babel/types@7.28.4':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
@@ -208,13 +208,13 @@ snapshots:
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.30
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/resolve-uri@3.1.2': {}
'@jridgewell/sourcemap-codec@1.5.5': {}
'@jridgewell/trace-mapping@0.3.30':
'@jridgewell/trace-mapping@0.3.31':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
@@ -230,9 +230,9 @@ snapshots:
'@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.6.2)':
dependencies:
'@babel/generator': 7.28.3
'@babel/parser': 7.28.3
'@babel/traverse': 7.28.3
'@babel/types': 7.28.2
'@babel/parser': 7.28.4
'@babel/traverse': 7.28.4
'@babel/types': 7.28.4
javascript-natural-sort: 0.7.1
lodash: 4.17.21
prettier: 3.6.2
@@ -241,7 +241,7 @@ snapshots:
asap@2.0.6: {}
debug@4.4.1:
debug@4.4.3:
dependencies:
ms: 2.1.3

View File

@@ -15,23 +15,22 @@
; my_build_flags = -DEMSESP_TEST -DEMSESP_DEBUG
; my_build_flags = -DEMSESP_DEBUG -DEMSESP_TEST -DEMSESP_PINGTEST
; my_build_flags = -DEMSESP_DEBUG -DCORE_DEBUG_LEVEL=5
; my_build_flags = -DEMSESP_EN_ONLY -DEMSESP_TEST -DCORE_DEBUG_LEVEL=5 -DARDUINO_USB_CDC_ON_BOOT=1
my_build_flags =
[platformio]
default_envs = s_16M_P ; BBQKees E32V2
; default_envs = s_16M_P ; BBQKees E32V2
; default_envs = s3_16M_P ; BBQKees S3
; default_envs = s_4M ; BBQKees older S32, 4MB no psram
; default_envs = s_16M ; BBQKees newer S32 V2, 16MB no psram
; default_envs = c6 ; XIAO ESP32C
default_envs = debug
[env]
; uncomment this section to enable extra debug
; build_type = debug
; monitor_filters = colorize, esp32_exception_decoder
; uncomment if you want to upload the firmware via OTA (must have upload_protocol = custom)
extra_scripts =
; pre:scripts/build_interface.py ; builds the WebUI (unless NO_BUILD_WEBUI is set) - comment out if you don't want to build each time
pre:scripts/build_interface.py ; builds the WebUI (unless NO_BUILD_WEBUI is set) - comment out if you don't want to build each time
scripts/rename_fw.py ; renames the firmware .bin file - comment out if not needed
scripts/upload.py ; optionally upload the firmware via OTA (must have upload_protocol = custom)
@@ -47,47 +46,48 @@ custom_password = admin
; upload_protocol = custom
; custom_emsesp_ip = <ip address> or ems-esp.local
upload_protocol = custom
; upload_protocol = custom
; custom_emsesp_ip = 10.10.10.93 ; S3
custom_emsesp_ip = 192.168.1.223 ; E32V2
; custom_emsesp_ip = 192.168.1.173 ; S32
; custom_emsesp_ip = 192.168.1.59 ; S32 (old) 4MB blue board
lib_deps =
bblanchon/ArduinoJson @ 7.4.2
ESP32Async/AsyncTCP @ 3.4.7
ESP32Async/ESPAsyncWebServer @ 3.8.0
file://${PROJECT_DIR}/../modules/EMS-ESP-Modules
; ; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
; lib_deps =
; bblanchon/ArduinoJson @ 7.4.2
; ESP32Async/AsyncTCP @ 3.4.9
; ESP32Async/ESPAsyncWebServer @ 3.8.1
; ; file://${PROJECT_DIR}/../modules/EMS-ESP-Modules
; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
[espressif32_base_16M]
; [espressif32_base_16M]
; framework = arduino
; board_build.partitions = partitions/esp32_partition_16M.csv
; board_upload.flash_size = 16MB
; board_build.app_partition_name = app0
; ; platform = espressif32@6.12.0 ; Arduino Core v2.0.17 / IDF v4.4.7
; ; platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip ; Arduino Core v3.2.0 / ESP-IDF v5.4.1
; platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip ; Arduino Core 3.3.0, IDF 5.5.0
; ** debug settings **
; to be used with an ESP32-S3 which has an onboard JTAG, connect the OTG USB port to the PC
; if using an external JTAG board like the ESP-PROG set debug_tool = esp-prog, and use zadig to set the correct driver for the USB port
; see https://docs.platformio.org/en/stable/projectconf/sections/env/options/debug/debug_init_break.html for settings
; use with my_build_flags = -DEMSESP_EN_ONLY -DEMSESP_TEST -DCORE_DEBUG_LEVEL=5 -DARDUINO_USB_CDC_ON_BOOT=1
[env:debug]
board = esp32-s3-devkitc-1
framework = arduino
platform = espressif32@6.12.0 ; Arduino Core v2.0.17 / IDF v4.4.7
board_build.partitions = partitions/esp32_partition_16M.csv
board_upload.flash_size = 16MB
board_build.app_partition_name = app0
; platform = espressif32@6.12.0 ; Arduino Core v2.0.17 / IDF v4.4.7
; platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip ; Arduino Core v3.2.0 / ESP-IDF v5.4.1
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip ; Arduino Core 3.3.0, IDF 5.5.0
; ** debug settings **
; to be used with esp-prog/JTAG hardware device like https://docs.espressif.com/projects/esp-dev-kits/en/latest/other/esp-prog/user_guide.html
; pio run -e debug
; add to build_flags these additional options: EMSESP_DEBUG EMSESP_UART_DEBUG EMSESP_DEBUG_SENSOR
[env:debug]
board = esp32dev
framework = arduino
platform = espressif32
board_build.partitions = partitions/esp32_partition_debug.csv
board_upload.flash_size = 4MB
board_build.filesystem = littlefs
upload_protocol = esptool
build_type = debug
monitor_raw = no
monitor_filters = esp32_exception_decoder
debug_tool = esp-prog
debug_init_break = tbreak setup
build_flags = ${factory_settings.build_flags} -DEMSESP_EN_ONLY -DCORE_DEBUG_LEVEL=5 -DONEWIRE_CRC16=0 -DNO_GLOBAL_ARDUINOOTA -DARDUINOJSON_ENABLE_STD_STRING=1 -DESP32=1 -DARDUINO_ARCH_ESP32=1
upload_port = /dev/ttyUSB0
monitor_filters = colorize, esp32_exception_decoder
debug_tool = esp-builtin
debug_init_break =
upload_speed = 2000000
monitor_speed = 115200
extra_scripts =
scripts/rename_fw.py
post:scripts/app-tls-size.py
; debug_build_flags = -Og -g2 -ggdb2

View File

@@ -40,13 +40,12 @@ build_flags =
-D CONFIG_ASYNC_TCP_PRIORITY=10 ; default
-D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 ; default
-D CONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core)
-D CONFIG_ASYNC_TCP_STACK_SIZE=6144 ; stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k - (default is 16K)
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192 ; stack usage measured: ESP32: ~2.3K, ESP32S3: ~3.5k - (was 6KB/6144, default is 16KB/8192*2)
; ESPAsyncWebServer
; -D WS_MAX_QUEUED_MESSAGES=0 ; not used, default 8
; -D SSE_MAX_QUEUED_MESSAGES=1 ; for log messages, default 32
-D CORE_DEBUG_LEVEL=0
-D EMSESP_SCHEDULER_RUNNING_CORE=1
-D EMSESP_SCHEDULER_STACKSIZE=8192
-D EMSESP_SCHEDULER_STACKSIZE=8192 ; 8KB
-D EMSESP_MQTT_RUNNING_CORE=1 ; default 1
; -D EMSESP_MQTT_STACKSIZE=5120 ; default
-D EMSESP_UART_RUNNING_CORE=1 ; default any core
@@ -61,7 +60,7 @@ framework = arduino
board_build.partitions = partitions/esp32_partition_16M.csv
board_upload.flash_size = 16MB
board_build.app_partition_name = app0
platform = espressif32@6.12.0 ; Arduino Core v2.0.17 / IDF v4.4.7
platform = espressif32@6.12.0 ; Arduino Core 2.0.17 / IDF 4.4.7
; 32MB Flash variants
[espressif32_base_32M]
@@ -108,8 +107,8 @@ build_type = release
board_build.filesystem = littlefs
lib_deps =
bblanchon/ArduinoJson @ 7.4.2
ESP32Async/AsyncTCP @ 3.4.7
ESP32Async/ESPAsyncWebServer @ 3.8.0
ESP32Async/AsyncTCP @ 3.4.9
ESP32Async/ESPAsyncWebServer @ 3.8.1
https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8
;
@@ -228,7 +227,7 @@ lib_deps =
; unit tests
; The code is in ./test/test_api.*
; to run use `pio run -e native-test -t exec`. All tests should PASS.
; to run use `platformio run -e native-test -t exec`. All tests should PASS.
; to update the test results, compile with -DEMSESP_UNITY_CREATE by uncommenting the line below
; then re-run and capture the output between "START - CUT HERE" and "END - CUT HERE" into the test_api.h file
[env:native-test]
@@ -236,17 +235,17 @@ platform = native
extra_scripts =
test_build_src = true
build_flags =
; -DEMSESP_UNITY_CREATE
-DARDUINOJSON_ENABLE_ARDUINO_STRING=1
-DEMSESP_STANDALONE -DEMSESP_TEST
-DEMSESP_UNITY
; -DEMSESP_UNITY_CREATE
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.2-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.3-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\"
-std=gnu++17 -Og -ggdb
-lgcov --coverage -fprofile-arcs -ftest-coverage
build_type = debug
build_src_flags =
-Wall -Wextra
-Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces
-Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare
-I./src/core
-I./lib_standalone
-I./lib/uuid-common/src

View File

@@ -1413,4 +1413,9 @@ temprature
bblanchon
vacationmode
airbypass
Suprapur
Suprapur
mfgadd
shuntingyard
XIAO
zadig
devkitc

View File

@@ -3,6 +3,6 @@ sonar.projectKey=emsesp_EMS-ESP32
sonar.projectName=EMS-ESP32
sonar.projectVersion=3.7.3
sonar.sources=./src
sonar.cfamily.compile-commands=compile_commands.json
sonar.cfamily.compile-commands=bw-output/compile_commands.json
sonar.sourceEncoding=UTF-8
sonar.host.url=https://sonarcloud.io

View File

@@ -746,7 +746,7 @@ bool AnalogSensor::get_value_info(JsonObject output, const char * cmd, const int
// match custom name or sensor GPIO
if (cmd == Helpers::toLower(sensor.name()) || Helpers::atoint(cmd) == sensor.gpio()) {
get_value_json(output, sensor);
return Command::set_attribute(output, cmd, attribute_s);
return Command::get_attribute(output, cmd, attribute_s);
}
}

View File

@@ -143,7 +143,8 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
}
// check if data is entity like device/hc/name/value
if (data.is<const char *>()) {
// unless the command is system/message
if ((strcmp(command_p, "message") != 0) && data.is<const char *>()) {
const char * d = data.as<const char *>();
if (strlen(d)) {
char * device_end = (char *)strchr(d, '/');
@@ -183,9 +184,9 @@ uint8_t Command::process(const char * path, const bool is_admin, const JsonObjec
}
}
// call the command based on the type
uint8_t return_code = CommandRet::OK;
// call the command based on the type
if (data.is<const char *>()) {
return_code = Command::call(device_type, command_p, data.as<const char *>(), is_admin, id_n, output);
} else if (data.is<int>()) {
@@ -289,8 +290,12 @@ const char * Command::parse_command_string(const char * command, int8_t & id) {
return command;
}
// check if command contains an attribute
// check if command string contains an attribute and returns it
const char * Command::get_attribute(const char * cmd) {
if (cmd == nullptr) {
return nullptr;
}
char * breakp = (char *)strchr(cmd, '/');
if (breakp) {
*breakp = '\0';
@@ -299,9 +304,9 @@ const char * Command::get_attribute(const char * cmd) {
return nullptr;
}
// returns the attribute in the given JSON object as a key/value pair called api_data
// get the attribute in the given JSON object as a key/value pair called api_data
// or errors if the attribute is not found
bool Command::set_attribute(JsonObject output, const char * cmd, const char * attribute) {
bool Command::get_attribute(JsonObject output, const char * cmd, const char * attribute) {
if (attribute == nullptr) {
return true;
}
@@ -325,7 +330,8 @@ bool Command::set_attribute(JsonObject output, const char * cmd, const char * at
char error[100];
snprintf(error, sizeof(error), "no attribute '%s' in %s", attribute, cmd);
output["message"] = error;
return false;
return false; // fail
}
// calls a command directly

View File

@@ -137,7 +137,7 @@ class Command {
static const char * parse_command_string(const char * command, int8_t & id);
static const char * get_attribute(const char * cmd);
static bool set_attribute(JsonObject output, const char * cmd, const char * attribute);
static bool get_attribute(JsonObject output, const char * cmd, const char * attribute);
static const char * return_code_string(const uint8_t return_code);

View File

@@ -1534,7 +1534,7 @@ bool EMSdevice::get_value_info(JsonObject output, const char * cmd, const int8_t
get_value_json(output, dv);
// if we're filtering on an attribute, go find it
// if we can't find it, maybe it exists but doesn't not have a value assigned yet
return Command::set_attribute(output, cmd_s, attribute_s);
return Command::get_attribute(output, cmd_s, attribute_s);
}
}
return false; // not found, but don't return a message error yet

View File

@@ -549,7 +549,7 @@ class EMSdevice {
std::vector<uint16_t> handlers_ignored_;
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST)
public: // so we can call it from WebCustomizationService::test() and EMSESP::dump_all_entities()
public: // so we can call it from WebCustomizationService::load_test_data() and EMSESP::dump_all_entities()
#endif
std::vector<TelegramFunction> telegram_functions_; // each EMS device has its own set of registered telegram types
std::vector<DeviceValue> devicevalues_; // all the device values

View File

@@ -29,7 +29,7 @@ static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe");
namespace emsesp {
// Static member definitions
std::deque<std::unique_ptr<EMSdevice>> EMSESP::emsdevices;
std::deque<std::unique_ptr<EMSdevice>> EMSESP::emsdevices{};
std::vector<EMSESP::Device_record> EMSESP::device_library_;
uuid::log::Logger EMSESP::logger_{F_(emsesp), uuid::log::Facility::KERN};
uint16_t EMSESP::watch_id_ = WATCH_ID_NONE;
@@ -1615,7 +1615,7 @@ void EMSESP::start() {
serial_console_.begin(SERIAL_CONSOLE_BAUD_RATE);
// always start a serial console if we're running standalone, except if we're running unit tests
#if defined(EMSESP_STANDALONE) || defined(EMSESP_DEBUG)
#if defined(EMSESP_STANDALONE) || defined(EMSESP_TEST) || defined(EMSESP_DEBUG)
#ifndef EMSESP_UNITY
start_serial_console();
#endif
@@ -1723,7 +1723,11 @@ void EMSESP::start() {
void EMSESP::start_serial_console() {
shell_ = std::make_shared<EMSESPConsole>(*this, serial_console_, true);
#if defined(EMSESP_STANDALONE)
shell_->maximum_log_messages(500);
#else
shell_->maximum_log_messages(100);
#endif
shell_->start();
#if defined(EMSESP_DEBUG)
shell_->log_level(uuid::log::Level::DEBUG);

View File

@@ -74,7 +74,7 @@ MAKE_WORD_TRANSLATION(watch_cmd, "watch incoming telegrams", "Beobachte eingehen
MAKE_WORD_TRANSLATION(publish_cmd, "publish all to MQTT", "Publiziere MQTT", "publiceer alles naar MQTT", "publicera allt till MQTT", "opublikuj wszystko na MQTT", "Publiser alt til MQTT", "", "Hepsini MQTTye gönder", "pubblica tutto su MQTT", "zverejniť všetko na MQTT", "publikovat vše do MQTT") // TODO translate
MAKE_WORD_TRANSLATION(system_info_cmd, "show system info", "Zeige Systeminformationen", "toon systeemstatus", "visa systeminformation", "pokaż status systemu", "vis system status", "", "Sistem Durumunu Göster", "visualizza stati di sistema", "zobraziť stav systému", "zobrazit informace o systému") // TODO translate
MAKE_WORD_TRANSLATION(schedule_cmd, "enable schedule item", "Aktiviere Zeitplanelemente", "activeer tijdschema item", "aktivera schemalagt objekt", "aktywuj wybrany harmonogram", "", "", "program öğesini etkinleştir", "abilitare l'elemento programmato", "povoliť položku plánovania", "povolit položku plánování") // TODO translate
MAKE_WORD_TRANSLATION(entity_cmd, "set custom value on ems", "Sende eigene Entitäten zu EMS", "verstuur custom waarde naar EMS", "sätt ett eget värde i EMS", "wyślij własną wartość na EMS", "", "", "emp üzerinde özel değer ayarla", "imposta valori personalizzati su EMS", "nastaviť vlastnú hodnotu na ems", "nastavit vlastní hodnotu na ems") // TODO translate
MAKE_WORD_TRANSLATION(entity_cmd, "set custom value", "Sende eigene Entitäten", "verstuur custom waarde", "sätt ett eget värde", "wyślij własną wartość", "", "", "emp üzerinde özel değer ayarla", "imposta valori personalizzati", "nastaviť vlastnú hodnotu", "nastavit vlastní hodnotu") // TODO translate
MAKE_WORD_TRANSLATION(commands_response, "get response", "Hole Antwort", "Verzoek om antwoord", "hämta svar", "uzyskaj odpowiedź", "", "", "gelen cevap", "", "získať odpoveď", "získat odpověď") // TODO translate
MAKE_WORD_TRANSLATION(coldshot_cmd, "send a cold shot of water", "Zugabe einer Menge kalten Wassers", "", "sckicka en liten mängd kallvatten", "uruchom tryśnięcie zimnej wody", "", "", "soğuk su gönder", "", "pošlite studenú dávku vody", "poslat studenou vodu") // TODO translate
MAKE_WORD_TRANSLATION(message_cmd, "send a message", "Eine Nachricht senden", "", "skicka ett meddelande", "", "", "", "", "", "poslať správu", "odeslat zprávu") // TODO translate

View File

@@ -125,13 +125,13 @@ void Mqtt::resubscribe() {
// Main MQTT loop - sends out top item on publish queue
void Mqtt::loop() {
queuecount_ = mqttClient_->queueSize();
// exit if MQTT is not enabled or if there is no network connection
if (!connected()) {
return;
}
queuecount_ = mqttClient_->queueSize();
uint32_t currentMillis = uuid::get_uptime();
// send heartbeat

View File

@@ -17,37 +17,10 @@
// copy from https://gist.github.com/t-mat/b9f681b7591cdae712f6
// modified MDvP, 06.2024
//
#include <string>
#include <vector>
#include <deque>
#include <math.h>
class Token {
public:
enum class Type : uint8_t {
Unknown,
Number,
String,
Operator,
Compare,
Logic,
Unary,
LeftParen,
RightParen,
};
#include "emsesp.h"
Token(Type type, const std::string & s, int8_t precedence = -1, bool rightAssociative = false)
: type{type}
, str(s)
, precedence{precedence}
, rightAssociative{rightAssociative} {
}
const Type type;
const std::string str;
const int8_t precedence;
const bool rightAssociative;
};
#include "shuntingYard.h"
// find tokens
std::deque<Token> exprToTokens(const std::string & expr) {
@@ -67,7 +40,12 @@ std::deque<Token> exprToTokens(const std::string & expr) {
if (*p) {
++p;
}
const auto s = std::string(b, p);
auto s = std::string(b, p);
auto n = s.find("\"\"");
while (n != std::string::npos) {
s.erase(n, 2);
n = s.find("\"\"");
}
tokens.emplace_back(Token::Type::String, s, -3);
if (*p == '\0') {
--p;
@@ -357,11 +335,11 @@ bool isnum(const std::string & s) {
// replace commands like "<device>/<hc>/<cmd>" with its value"
std::string commands(std::string & expr, bool quotes = true) {
std::string commands(std::string & expr, bool quotes) {
auto expr_new = emsesp::Helpers::toLower(expr);
for (uint8_t device = 0; device < emsesp::EMSdevice::DeviceType::UNKNOWN; device++) {
const char * d = emsesp::EMSdevice::device_type_2_device_name(device);
auto f = expr_new.find(d);
std::string d = (std::string)emsesp::EMSdevice::device_type_2_device_name(device) + "/";
auto f = expr_new.find(d);
while (f != std::string::npos) {
// entity names are alphanumeric or _
auto e = expr_new.find_first_not_of("/._abcdefghijklmnopqrstuvwxyz0123456789", f);
@@ -375,6 +353,7 @@ std::string commands(std::string & expr, bool quotes = true) {
}
expr_new.copy(cmd, l, f);
cmd[l] = '\0';
if (strstr(cmd, "/value") == nullptr) {
strlcat(cmd, "/value", sizeof(cmd) - 6);
}
@@ -401,6 +380,14 @@ std::string commands(std::string & expr, bool quotes = true) {
f = expr_new.find(d, e);
}
}
if (quotes) {
// remove double quotes
auto f = expr.find("\"\"");
while (f != std::string::npos) {
expr.erase(f, 2);
f = expr.find("\"\"");
}
}
return expr;
}
@@ -442,26 +429,24 @@ std::string to_hex(uint32_t i) {
// RPN calculator
std::string calculate(const std::string & expr) {
std::string expr_new = expr;
commands(expr_new);
// commands(expr_new);
const auto tokens = exprToTokens(expr_new);
// for debugging only
// for (const auto & t : tokens) {
// emsesp::EMSESP::logger().debug("shunt token: %s(%d)", t.str.c_str(), t.type);
// Serial.printf("shunt token: %s(%d)\n", t.str.c_str(), t.type);
// Serial.println();
// }
if (tokens.empty()) {
return "";
}
auto queue = shuntingYard(tokens);
if (queue.empty()) {
return "";
}
/*
// debug only print tokens
#ifdef EMSESP_STANDALONE
for (const auto & t : queue) {
emsesp::EMSESP::logger().debug("shunt token: %s(%d)", t.str.c_str(), t.type);
}
#endif
*/
std::vector<std::string> stack;
while (!queue.empty()) {
@@ -679,6 +664,7 @@ std::string calculate(const std::string & expr) {
// check for multiple instances of <cond> ? <expr1> : <expr2>
std::string compute(const std::string & expr) {
std::string expr_new = expr;
commands(expr_new); // replace ems-esp commands with values
// search json with url:
auto f = expr_new.find_first_of('{');

86
src/core/shuntingYard.h Normal file
View File

@@ -0,0 +1,86 @@
// Shunting-yard Algorithm
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
//
// Implementation notes for unary operators by Austin Taylor
// https://stackoverflow.com/a/5240912
//
// Example:
// https://ideone.com/VocUTq
//
// License:
// This code uses the following materials.
// (1) Wikipedia article [Shunting-yard algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm),
// which is released under the [Creative Commons Attribution-Share-Alike License 3.0](https://creativecommons.org/licenses/by-sa/3.0/).
// (2) [Implementation notes for unary operators in Shunting-Yard algorithm](https://stackoverflow.com/a/5240912) by Austin Taylor
// which is released under the [Creative Commons Attribution-Share-Alike License 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
//
// copy from https://gist.github.com/t-mat/b9f681b7591cdae712f6
// modified MDvP, 06.2024
//
#ifndef EMSESP_SHUNTING_YARD_H_
#define EMSESP_SHUNTING_YARD_H_
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <string>
#include <vector>
#include <deque>
#include <math.h>
class Token {
public:
enum class Type : uint8_t {
Unknown,
Number,
String,
Operator,
Compare,
Logic,
Unary,
LeftParen,
RightParen,
};
Token(Type type, const std::string & s, int8_t precedence = -1, bool rightAssociative = false)
: type{type}
, str(s)
, precedence{precedence}
, rightAssociative{rightAssociative} {
}
const Type type;
const std::string str;
const int8_t precedence;
const bool rightAssociative;
};
// find tokens
std::deque<Token> exprToTokens(const std::string & expr);
// sort tokens to RPN form
std::deque<Token> shuntingYard(const std::deque<Token> & tokens);
// check if string is a number
bool isnum(const std::string & s);
// replace commands like "<device>/<hc>/<cmd>" with its value"
std::string commands(std::string & expr, bool quotes = true);
// checks for logic value
int to_logic(const std::string & s);
// number to string, remove trailing zeros
std::string to_string(double d);
// number to hex string
std::string to_hex(uint32_t i);
// RPN calculator
std::string calculate(const std::string & expr);
// check for multiple instances of <cond> ? <expr1> : <expr2>
std::string compute(const std::string & expr);
#endif

View File

@@ -19,6 +19,8 @@
#include "system.h"
#include "emsesp.h" // for send_raw_telegram() command
#include "shuntingYard.h"
#ifndef EMSESP_STANDALONE
#include "esp_ota_ops.h"
#endif
@@ -48,8 +50,6 @@
#include <esp_mac.h>
#endif
#include <HTTPClient.h>
#ifndef EMSESP_STANDALONE
#include "esp_efuse.h"
#endif
@@ -205,14 +205,23 @@ bool System::command_syslog_level(const char * value, const int8_t id) {
}
*/
// send message - to log and MQTT
bool System::command_message(const char * value, const int8_t id) {
// send message - to system log and MQTT
bool System::command_message(const char * value, const int8_t id, JsonObject output) {
if (value == nullptr || value[0] == '\0') {
LOG_WARNING("Message is empty");
return false; // must have a string value
}
LOG_INFO("Message: %s", value);
Mqtt::queue_publish(F_(message), value);
auto computed_value = compute(value); // process the message via Shunting Yard
if (computed_value.length() == 0) {
LOG_WARNING("Message result is empty");
return false;
}
LOG_INFO("Message: %s", computed_value.c_str()); // send to log
Mqtt::queue_publish(F_(message), computed_value); // send to MQTT if enabled
output["api_data"] = computed_value; // send to API
return true;
}
@@ -831,9 +840,9 @@ void System::system_check() {
// everything is healthy, show LED permanently on or off depending on setting
if (led_gpio_) {
#if ESP_ARDUINO_VERSION_MAJOR < 3
led_type_ ? neopixelWrite(led_gpio_, 0, hide_led_ ? 0 : 128, 0) : digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
led_type_ ? neopixelWrite(led_gpio_, 0, hide_led_ ? 0 : RGB_LED_BRIGHTNESS, 0) : digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
#else
led_type_ ? rgbLedWrite(led_gpio_, 0, hide_led_ ? 0 : 128, 0) : digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
led_type_ ? rgbLedWrite(led_gpio_, 0, hide_led_ ? 0 : RGB_LED_BRIGHTNESS, 0) : digitalWrite(led_gpio_, hide_led_ ? !LED_ON : LED_ON);
#endif
}
} else {
@@ -913,36 +922,35 @@ void System::led_monitor() {
// 1 flash is the EMS bus is not connected
// 2 flashes if the network (wifi or ethernet) is not connected
// 3 flashes is both the bus and the network are not connected. Then you know you're truly f*cked.
if (led_type_) {
if (led_flash_step_ == 3) {
if ((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK) {
#if ESP_ARDUINO_VERSION_MAJOR < 3
neopixelWrite(led_gpio_, 128, 0, 0); // red
neopixelWrite(led_gpio_, RGB_LED_BRIGHTNESS, 0, 0); // red
#else
rgbLedWrite(led_gpio_, 128, 0, 0); // red
rgbLedWrite(led_gpio_, RGB_LED_BRIGHTNESS, 0, 0); // red
#endif
} else if ((healthcheck_ & HEALTHCHECK_NO_BUS) == HEALTHCHECK_NO_BUS) {
#if ESP_ARDUINO_VERSION_MAJOR < 3
neopixelWrite(led_gpio_, 0, 0, 128); // blue
neopixelWrite(led_gpio_, 0, 0, RGB_LED_BRIGHTNESS); // blue
#else
rgbLedWrite(led_gpio_, 0, 0, 128); // blue
rgbLedWrite(led_gpio_, 0, 0, RGB_LED_BRIGHTNESS); // blue
#endif
}
}
if (led_flash_step_ == 5 && (healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK) {
#if ESP_ARDUINO_VERSION_MAJOR < 3
neopixelWrite(led_gpio_, 128, 0, 0); // red
neopixelWrite(led_gpio_, RGB_LED_BRIGHTNESS, 0, 0); // red
#else
rgbLedWrite(led_gpio_, 128, 0, 0); // red
rgbLedWrite(led_gpio_, RGB_LED_BRIGHTNESS, 0, 0); // red
#endif
}
if ((led_flash_step_ == 7) && ((healthcheck_ & HEALTHCHECK_NO_NETWORK) == HEALTHCHECK_NO_NETWORK)
&& ((healthcheck_ & HEALTHCHECK_NO_BUS) == HEALTHCHECK_NO_BUS)) {
#if ESP_ARDUINO_VERSION_MAJOR < 3
neopixelWrite(led_gpio_, 0, 0, 128); // blue
neopixelWrite(led_gpio_, 0, 0, RGB_LED_BRIGHTNESS); // blue
#else
rgbLedWrite(led_gpio_, 0, 0, 128); // blue
rgbLedWrite(led_gpio_, 0, 0, RGB_LED_BRIGHTNESS); // blue
#endif
}
} else {
@@ -1433,7 +1441,6 @@ bool System::command_service(const char * cmd, const char * value) {
if (!strcmp(cmd, "fuse/mfg")) {
ok = esp_efuse_write_reg(EFUSE_BLK3, 0, (uint32_t)n) == ESP_OK;
ok ? LOG_INFO("fuse programed with value '%X': successful", n) : LOG_ERROR("fuse programed with value '%X': failed", n);
}
if (!strcmp(cmd, "fuse/mfgadd")) {
uint8_t reg = 0;
@@ -1521,7 +1528,7 @@ bool System::get_value_info(JsonObject output, const char * cmd) {
return true;
}
}
} // else skipt, but we don't have value pairs in system root
} // else skip, but we don't have value pairs in system root
}
}
return false;
@@ -1569,11 +1576,11 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
node["sdk"] = ESP.getSdkVersion();
node["freeMem"] = getHeapMem();
node["maxAlloc"] = getMaxAllocMem();
node["freeCaps"] = heap_caps_get_free_size(MALLOC_CAP_8BIT) / 1024; // includes heap and psram
node["usedApp"] = EMSESP::system_.appUsed(); // kilobytes
node["freeApp"] = EMSESP::system_.appFree(); // kilobytes
node["partition"] = esp_ota_get_running_partition()->label; // active partition
node["flash_chip_size"] = ESP.getFlashChipSize() / 1024; // kilobytes
node["freeCaps"] = heap_caps_get_free_size(MALLOC_CAP_8BIT) / 1024; // includes heap and psram
node["usedApp"] = EMSESP::system_.appUsed(); // kilobytes
node["freeApp"] = EMSESP::system_.appFree(); // kilobytes
node["partition"] = (const char *)esp_ota_get_running_partition()->label; // active partition
node["flash_chip_size"] = ESP.getFlashChipSize() / 1024; // kilobytes
#endif
node["resetReason"] = EMSESP::system_.reset_reason(0) + " / " + EMSESP::system_.reset_reason(1);
#ifndef EMSESP_STANDALONE
@@ -1842,6 +1849,7 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output
}
}
}
// Also show EMSESP devices if we have any
if (EMSESP::temperaturesensor_.count_entities()) {
JsonObject obj = devices.add<JsonObject>();

View File

@@ -55,6 +55,8 @@ using uuid::console::Shell;
#define EMSESP_CUSTOMSUPPORT_FILE "/config/customSupport.json"
#define RGB_LED_BRIGHTNESS 20
namespace emsesp {
enum PHY_type : uint8_t { PHY_TYPE_NONE = 0, PHY_TYPE_LAN8720, PHY_TYPE_TLK110 };
@@ -83,7 +85,7 @@ class System {
static bool command_restart(const char * value, const int8_t id);
static bool command_format(const char * value, const int8_t id);
static bool command_watch(const char * value, const int8_t id);
static bool command_message(const char * value, const int8_t id);
static bool command_message(const char * value, const int8_t id, JsonObject output);
static bool command_info(const char * value, const int8_t id, JsonObject output);
static bool command_response(const char * value, const int8_t id, JsonObject output);
static bool command_service(const char * cmd, const char * value);

View File

@@ -399,7 +399,7 @@ bool TemperatureSensor::get_value_info(JsonObject output, const char * cmd, cons
// match custom name or sensor ID
if (cmd == Helpers::toLower(sensor.name()) || cmd == Helpers::toLower(sensor.id())) {
get_value_json(output, sensor);
return Command::set_attribute(output, cmd, attribute_s);
return Command::get_attribute(output, cmd, attribute_s);
}
}
@@ -618,7 +618,9 @@ bool TemperatureSensor::Sensor::apply_customization() {
// hard coded tests
#if defined(EMSESP_TEST)
void TemperatureSensor::test() {
void TemperatureSensor::load_test_data() {
sensors_.clear(); // delete all existing sensors
// add 2 temperature sensors
// Sensor ID: 01_0203_0405_0607
uint8_t addr[ADDR_LEN] = {1, 2, 3, 4, 5, 6, 7, 8};

View File

@@ -113,7 +113,7 @@ class TemperatureSensor {
bool update(const std::string & id, const std::string & name, int16_t offset);
#if defined(EMSESP_TEST)
void test();
void load_test_data();
#endif
private:

View File

@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.7.3-dev.17"
#define EMSESP_APP_VERSION "3.7.3-dev.18"

View File

@@ -327,10 +327,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// add devices
test("general");
EMSESP::webCustomEntityService.test(); // custom entities
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.test(); // add temperature sensors
EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions
EMSESP::webCustomEntityService.load_test_data(); // custom entities
EMSESP::webCustomizationService.load_test_data(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.load_test_data(); // add temperature sensors
EMSESP::webSchedulerService.load_test_data(); // add scheduler data
shell.invoke_command("show values");
ok = true;
@@ -348,7 +348,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Adding custom entities...");
// add some dummy entities
EMSESP::webCustomEntityService.test();
EMSESP::webCustomEntityService.load_test_data();
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
@@ -372,15 +372,13 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
char data[] = "{\"value\":\"99\"}";
deserializeJson(doc, data);
json = doc.as<JsonVariant>();
// validate
request.url("/api/custom/test_ram");
EMSESP::webAPIService.webAPIService(&request, json);
// validate by showing values
request.method(HTTP_GET);
request.url("/api/custom");
EMSESP::webAPIService.webAPIService(&request);
#endif
ok = true;
}
@@ -389,7 +387,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.printfln("Adding Scheduler items...");
// add some dummy entities
EMSESP::webSchedulerService.test();
EMSESP::webSchedulerService.load_test_data();
#ifdef EMSESP_STANDALONE
AsyncWebServerRequest request;
@@ -832,7 +830,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// load some EMS data
// test("general");
emsesp::EMSESP::temperaturesensor_.test();
emsesp::EMSESP::temperaturesensor_.load_test_data();
shell.invoke_command("call temperaturesensor");
shell.invoke_command("show values");
@@ -857,7 +855,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
Mqtt::nested_format(1);
// Mqtt::nested_format(0);
emsesp::EMSESP::temperaturesensor_.test();
emsesp::EMSESP::temperaturesensor_.load_test_data();
shell.invoke_command("show values");
shell.invoke_command("call system publish");
@@ -879,7 +877,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// load some EMS data
test("general");
EMSESP::webCustomizationService.test(); // load the analog sensors
EMSESP::webCustomizationService.load_test_data(); // load the analog sensors
shell.invoke_command("call analogsensor");
shell.invoke_command("show values");
@@ -1073,6 +1071,156 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
shell.invoke_command("call boiler circpump/value");
}
if (command == "shuntingyard") {
shell.printfln("Testing shunting yard...");
// load devices
test("general");
EMSESP::webCustomEntityService.load_test_data(); // custom entities
EMSESP::webCustomizationService.load_test_data(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.load_test_data(); // add temperature sensors
EMSESP::webSchedulerService.load_test_data(); // run scheduler tests, and conditions
JsonDocument doc;
AsyncWebServerRequest request;
// request.method(HTTP_GET);
// request.url("/api/custom/test_seltemp/val");
// EMSESP::webAPIService.webAPIService(&request);
// set a custom value
// request.method(HTTP_POST);
// char data0[] = "{\"value\":\"99\"}";
// deserializeJson(doc, data0);
// JsonVariant json = doc.as<JsonVariant>();
// request.url("/api/custom/test_seltemp");
// EMSESP::webAPIService.webAPIService(&request, json);
// Note, works in test not in production - causes a crash!
// request.method(HTTP_POST);
// char data1[] = "{\"value\":\"system/settings/locale\"}";
// deserializeJson(doc, data1);
// JsonVariant json = doc.as<JsonVariant>();
// request.url("/api/system/message");
// EMSESP::webAPIService.webAPIService(&request, json);
// output: 70.00
request.method(HTTP_POST);
char data1[] = "{\"value\":\"custom/test_custom\"}";
deserializeJson(doc, data1);
JsonVariant json = doc.as<JsonVariant>();
request.url("/api/system/message");
EMSESP::webAPIService.webAPIService(&request, json);
request.method(HTTP_POST);
request.url("/api/system/message");
// output: hello world!
EMSESP::webAPIService.webAPIService(&request, "'hello world!'");
// output: hello world!
EMSESP::webAPIService.webAPIService(&request, "\"hello world!\"");
// output: helloworld
EMSESP::webAPIService.webAPIService(&request, "hello world");
// output: en
EMSESP::webAPIService.webAPIService(&request, "system/settings/locale");
// output: locale is en
EMSESP::webAPIService.webAPIService(&request, "'locale is 'system/settings/locale");
// output: locale is en
EMSESP::webAPIService.webAPIService(&request, "'locale is '(system/settings/locale)");
// output: rssi is -23
EMSESP::webAPIService.webAPIService(&request, "'rssi is '0+system/network/rssi");
// output: rssi is -23 dBm
EMSESP::webAPIService.webAPIService(&request, "'rssi is '(system/network/rssi)' dBm'");
// output: 14
EMSESP::webAPIService.webAPIService(&request, "custom/test_seltemp");
// output: 14
EMSESP::webAPIService.webAPIService(&request, "custom/test_seltemp/value");
// output: seltemp=14
EMSESP::webAPIService.webAPIService(&request, "'seltemp='custom/test_seltemp/value");
// output: 14
EMSESP::webAPIService.webAPIService(&request, "custom/test_seltemp");
// output: 40
EMSESP::webAPIService.webAPIService(&request, "boiler/flowtempoffset");
// output: 40
EMSESP::webAPIService.webAPIService(&request, "boiler/flowtempoffset/value");
// output: -67.8
EMSESP::webAPIService.webAPIService(&request, "(custom/test_seltemp - boiler/flowtempoffset) * 2.8 + 5");
// output: 1
EMSESP::webAPIService.webAPIService(&request, "thermostat/hc1/modetype == 'comfort'");
// output: 1
EMSESP::webAPIService.webAPIService(&request, "thermostat/hc1/modetype == 'Comfort'");
// output: 0
EMSESP::webAPIService.webAPIService(&request, "thermostat/hc1/modetype == 'unknown'");
// output: 53.8
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp1/value");
// output: 53.8
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp1");
// Output is "comfort" == Comfort (because missing closing quote)
EMSESP::webAPIService.webAPIService(&request, "'thermostat/hc1/modetype == 'Comfort'");
// output: 14
EMSESP::webAPIService.webAPIService(&request, "custom/test_seltemp");
//
// these next tests should fail or give warnings or strange results
//
// check when entity has no value, should pass (storagetemp2 has no value set)
// failed with no entity 'storagetemp' in boiler, Message result is empty
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp2 == \"\"");
// check when entity has no value, should pass (storagetemp2 has no value set)
// failed with no entity 'storagetemp' in boiler, Message result is empty
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp2 == ''");
// storagetemp2 has no value set
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp2");
// can't find entity, should fail with no entity 'storagetemp' in boiler
// output: "" Message result is empty
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp/value1");
// can't find entity, should fail with no attribute 'value1' in storagetemp1
// output: "" Message result is empty
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp1/value1");
// check when entity has no value, should pass (storagetemp2 has no value set)
// output: "" Message result is empty
EMSESP::webAPIService.webAPIService(&request, "boiler/storagetemp2/value");
// can't set empty value! Message result is empty
EMSESP::webAPIService.webAPIService(&request, "/api/custom/test_seltemp/val");
// test HTTP POST to call HA script
// test_cmd = "{\"method\":\"POST\",\"url\":\"http://192.168.1.42:8123/api/services/script/test_notify2\", \"header\":{\"authorization\":\"Bearer "
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMmNlYWI5NDgzMmI0ODE2YWQ2NzU4MjkzZDE2YWMxZSIsImlhdCI6MTcyMTM5MTI0NCwiZXhwIjoyMDM2NzUxMjQ0fQ."
// "S5sago1tEI6lNhrDCO0dM_WsVQHkD_laAjcks8tWAqo\"}}";
// command("test99", test_cmd.c_str(), "");
ok = true;
}
if (command == "api3") {
shell.printfln("Testing API getting values from system");
EMSESP::system_.bool_format(BOOL_FORMAT_TRUEFALSE); // BOOL_FORMAT_TRUEFALSE_STR
@@ -1081,8 +1229,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
bool single;
single = true;
// single = false;
// single = true;
single = false;
AsyncWebServerRequest request;
JsonDocument doc;
@@ -1096,10 +1244,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
if (single) {
// run dedicated tests only
// EMSESP::webCustomEntityService.test(); // custom entities
// EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
// EMSESP::temperaturesensor_.test(); // add temperature sensors
// EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions
// EMSESP::webCustomEntityService.load_test_data(); // custom entities
// EMSESP::webCustomizationService.load_test_data(); // set customizations - this will overwrite any settings in the FS
// EMSESP::temperaturesensor_.load_test_data(); // add temperature sensors
// EMSESP::webSchedulerService.load_test_data(); // run scheduler tests, and conditions
// request.url("/rest/deviceEntities");
// EMSESP::webCustomizationService.device_entities(&request);
@@ -1134,6 +1282,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
deserializeJson(doc, data0);
request.url("/api/thermostat/seltemp");
EMSESP::webAPIService.webAPIService(&request, doc.as<JsonVariant>());
// request.method(HTTP_GET);
// request.url("/api/thermostat/seltemp/value");
// EMSESP::webAPIService.webAPIService(&request);
@@ -1201,10 +1350,10 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
// shell.invoke_command("call system read \"8 2 27 1\"");
} else {
EMSESP::webCustomEntityService.test(); // custom entities
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.test(); // add temperature sensors
EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions
EMSESP::webCustomEntityService.load_test_data(); // custom entities
EMSESP::webCustomizationService.load_test_data(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.load_test_data(); // add temperature sensors
EMSESP::webSchedulerService.load_test_data(); // run scheduler tests, and conditions
request.method(HTTP_GET);
@@ -1249,7 +1398,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
EMSESP::webAPIService.webAPIService(&request);
request.url("/api/custom/info");
EMSESP::webAPIService.webAPIService(&request);
request.url("/api/custom/seltemp");
request.url("/api/custom/test_seltemp");
EMSESP::webAPIService.webAPIService(&request);
// system
@@ -1358,9 +1507,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const
EMSESP::webAPIService.webAPIService(&request);
// custom
request.url("/api/custom/seltemp2");
request.url("/api/custom/test_seltemp2");
EMSESP::webAPIService.webAPIService(&request);
request.url("/api/custom/seltemp/val");
request.url("/api/custom/test_seltemp/val");
EMSESP::webAPIService.webAPIService(&request);
// temperaturesensor

View File

@@ -61,7 +61,8 @@ namespace emsesp {
// #define EMSESP_DEBUG_DEFAULT "heat_exchange"
// #define EMSESP_DEBUG_DEFAULT "ls"
// #define EMSESP_DEBUG_DEFAULT "upload"
#define EMSESP_DEBUG_DEFAULT "hpmode"
// #define EMSESP_DEBUG_DEFAULT "hpmode"
#define EMSESP_DEBUG_DEFAULT "shuntingyard"
#ifndef EMSESP_DEBUG_DEFAULT
#define EMSESP_DEBUG_DEFAULT "general"

View File

@@ -51,6 +51,14 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant j
parse(request, input);
}
// for POSTS accepting plain text data
void WebAPIService::webAPIService(AsyncWebServerRequest * request, const char * data) {
JsonDocument input_doc;
JsonObject input = input_doc.to<JsonObject>();
input["data"] = data;
parse(request, input);
}
#ifdef EMSESP_TEST
// for test.cpp and unit tests so we can invoke GETs to test the API
void WebAPIService::webAPIService(AsyncWebServerRequest * request) {
@@ -100,7 +108,7 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
emsesp::EMSESP::system_.refreshHeapMem();
// output json buffer
auto response = new AsyncJsonResponse(false);
auto response = new AsyncJsonResponse();
// add more mem if needed - won't be needed in ArduinoJson 7
// while (!response->getSize()) {
@@ -155,10 +163,19 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) {
storeResponse(output);
#endif
#if defined(EMSESP_STANDALONE) && !defined(EMSESP_UNITY)
Serial.printf("%sweb output: %s[%s]", COLOR_WHITE, COLOR_BRIGHT_CYAN, request->url().c_str());
Serial.printf(" %s(%d)%s ", ret_codes[return_code] == 200 ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_RED, ret_codes[return_code], COLOR_YELLOW);
serializeJson(output, Serial);
Serial.println(COLOR_RESET);
std::string output_str;
serializeJson(output, output_str);
Serial.printf("%sweb output: %s[%s] %s(%d)%s %s%s",
COLOR_WHITE,
COLOR_BRIGHT_CYAN,
request->url().c_str(),
ret_codes[return_code] == 200 ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_RED,
ret_codes[return_code],
COLOR_YELLOW,
output_str.c_str(),
COLOR_RESET);
Serial.println();
EMSESP::logger().debug("web output: %s %s", request->url().c_str(), output_str.c_str());
#endif
}

View File

@@ -28,6 +28,7 @@ class WebAPIService {
WebAPIService(AsyncWebServer * server, SecurityManager * securityManager);
void webAPIService(AsyncWebServerRequest * request, JsonVariant input);
void webAPIService(AsyncWebServerRequest * request, const char * data); // for plain text data
#if defined(EMSESP_TEST)
// for test.cpp and running unit tests

View File

@@ -153,6 +153,12 @@ StateUpdateResult WebCustomEntity::update(JsonObject root, WebCustomEntity & web
// set value by api command
bool WebCustomEntityService::command_setvalue(const char * value, const int8_t id, const char * name) {
// don't write if there is no value, to prevent setting an empty value by mistake when parsing attributes
if (!strlen(value)) {
EMSESP::logger().debug("can't set empty value!");
return false;
}
for (CustomEntityItem & entityItem : *customEntityItems_) {
if (Helpers::toLower(entityItem.name) == Helpers::toLower(name)) {
if (entityItem.ram == 1) {
@@ -217,7 +223,7 @@ bool WebCustomEntityService::command_setvalue(const char * value, const int8_t i
// output of a single value
// if add_uom is true it will add the UOM string to the value
void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem & entity, const bool useVal, const bool web, const bool add_uom) {
void WebCustomEntityService::render_value(JsonObject output, CustomEntityItem const & entity, const bool useVal, const bool web, const bool add_uom) {
char payload[20];
std::string name = useVal ? "value" : entity.name;
switch (entity.value_type) {
@@ -286,6 +292,10 @@ void WebCustomEntityService::show_values(JsonObject output) {
// process json output for info/commands and value_info
bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd) {
if (cmd == nullptr || strlen(cmd) == 0) {
return false;
}
// if no custom entries, return empty json
// even if we're looking for a specific entity
// https://github.com/emsesp/EMS-ESP32/issues/1297
@@ -315,17 +325,17 @@ bool WebCustomEntityService::get_value_info(JsonObject output, const char * cmd)
// specific value info
const char * attribute_s = Command::get_attribute(cmd);
for (auto & entity : *customEntityItems_) {
for (auto const & entity : *customEntityItems_) {
if (Helpers::toLower(entity.name) == cmd) {
get_value_json(output, entity);
return Command::set_attribute(output, cmd, attribute_s);
return Command::get_attribute(output, cmd, attribute_s);
}
}
return false; // not found
}
// build the json for specific entity
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem & entity) {
void WebCustomEntityService::get_value_json(JsonObject output, CustomEntityItem const & entity) {
output["name"] = entity.name;
output["fullname"] = entity.name;
output["storage"] = entity.ram ? "ram" : "ems";
@@ -681,9 +691,10 @@ bool WebCustomEntityService::get_value(std::shared_ptr<const Telegram> telegram)
// hard coded tests
// add the entity and also add the command for writeable entities
#ifdef EMSESP_TEST
void WebCustomEntityService::test() {
void WebCustomEntityService::load_test_data() {
update([&](WebCustomEntity & webCustomEntity) {
webCustomEntity.customEntityItems.clear();
webCustomEntity.customEntityItems.clear(); // delete all existing entities
auto entityItem = CustomEntityItem();
// test 1
@@ -698,6 +709,7 @@ void WebCustomEntityService::test() {
entityItem.value_type = 1;
entityItem.writeable = true;
entityItem.data = "70";
entityItem.value = 70;
webCustomEntity.customEntityItems.push_back(entityItem);
Command::add(
EMSdevice::DeviceType::CUSTOM,
@@ -751,12 +763,12 @@ void WebCustomEntityService::test() {
entityItem.type_id = 0;
entityItem.offset = 0;
entityItem.factor = 1;
entityItem.name = "seltemp";
entityItem.name = "test_seltemp";
entityItem.uom = 0;
entityItem.value_type = 8;
entityItem.writeable = true;
entityItem.data = "14";
entityItem.value = 12;
entityItem.value = 14;
webCustomEntity.customEntityItems.push_back(entityItem);
Command::add(
EMSdevice::DeviceType::CUSTOM,

View File

@@ -60,10 +60,10 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
void publish(const bool force = false);
bool command_setvalue(const char * value, const int8_t id, const char * name);
bool get_value_info(JsonObject output, const char * cmd);
void get_value_json(JsonObject output, CustomEntityItem & entity);
void get_value_json(JsonObject output, CustomEntityItem const & entity);
bool get_value(std::shared_ptr<const Telegram> telegram);
void fetch();
void render_value(JsonObject output, CustomEntityItem & entity, const bool useVal = false, const bool web = false, const bool add_uom = false);
void render_value(JsonObject output, CustomEntityItem const & entity, const bool useVal = false, const bool web = false, const bool add_uom = false);
void show_values(JsonObject output);
void generate_value_web(JsonObject output, const bool is_dashboard = false);
@@ -73,7 +73,7 @@ class WebCustomEntityService : public StatefulService<WebCustomEntity> {
}
#if defined(EMSESP_TEST)
void test();
void load_test_data();
#endif
private:

View File

@@ -366,10 +366,11 @@ void WebCustomizationService::begin() {
// hard coded tests
#ifdef EMSESP_TEST
void WebCustomizationService::test() {
void WebCustomizationService::load_test_data() {
update([&](WebCustomization & webCustomization) {
// Temperature sensors
webCustomization.sensorCustomizations.clear();
webCustomization.sensorCustomizations.clear(); // delete all existing sensors
auto sensor = SensorCustomization();
sensor.id = "01_0203_0405_0607";
sensor.name = "test_tempsensor1";

View File

@@ -85,7 +85,7 @@ class WebCustomizationService : public StatefulService<WebCustomization> {
void begin();
#if defined(EMSESP_TEST)
void test();
void load_test_data();
#endif
// make all functions public so we can test in the debug and standalone mode

View File

@@ -18,7 +18,8 @@
#include "emsesp.h"
#include "WebSchedulerService.h"
#include <HTTPClient.h>
#include "shuntingYard.h"
namespace emsesp {
@@ -173,7 +174,7 @@ bool WebSchedulerService::get_value_info(JsonObject output, const char * cmd) {
for (const ScheduleItem & scheduleItem : *scheduleItems_) {
if (Helpers::toLower(scheduleItem.name) == cmd) {
get_value_json(output, scheduleItem);
return Command::set_attribute(output, cmd, attribute_s);
return Command::get_attribute(output, cmd, attribute_s);
}
}
@@ -321,14 +322,14 @@ uint8_t WebSchedulerService::count_entities(bool cmd_only) {
return count;
}
#include "shuntingYard.hpp"
// execute scheduled command
bool WebSchedulerService::command(const char * name, const std::string & command, const std::string & data) {
std::string cmd = Helpers::toLower(command);
// check http commands. e.g.
// tasmota(get): http://<tasmotsIP>/cm?cmnd=power%20ON
// tasmota(get): http://<tasmotaIP>/cm?cmnd=power%20ON
// shelly(get): http://<shellyIP>/relais/0?turn=on
// parse json
JsonDocument doc;
@@ -352,6 +353,7 @@ bool WebSchedulerService::command(const char * name, const std::string & command
std::string value = doc["value"] | data.c_str(); // extract value if its in the command, or take the data
std::string method = doc["method"] | "GET"; // default GET
commands(value, false);
// if there is data, force a POST
int httpResult = 0;
if (value.length() || method == "post") { // we have all lowercase
@@ -544,110 +546,38 @@ void WebSchedulerService::scheduler_task(void * pvParameters) {
// hard coded tests
#if defined(EMSESP_TEST)
void WebSchedulerService::test() {
static bool already_added = false;
if (!already_added) {
update([&](WebScheduler & webScheduler) {
// webScheduler.scheduleItems.clear();
// test 1
auto si = ScheduleItem();
si.active = true;
si.flags = 1;
si.time = "12:00";
si.cmd = "system/fetch";
si.value = "10";
si.name = "test_scheduler";
si.elapsed_min = 0;
si.retry_cnt = 0xFF; // no startup retries
void WebSchedulerService::load_test_data() {
update([&](WebScheduler & webScheduler) {
webScheduler.scheduleItems.clear(); // delete all existing schedules
webScheduler.scheduleItems.push_back(si);
// test 1
auto si = ScheduleItem();
si.active = true;
si.flags = 1;
si.time = "12:00";
si.cmd = "system/fetch";
si.value = "10";
si.name = "test_scheduler";
si.elapsed_min = 0;
si.retry_cnt = 0xFF; // no startup retries
// test 2
si = ScheduleItem();
si.active = false;
si.flags = 1;
si.time = "13:00";
si.cmd = "system/message";
si.value = "20";
si.name = ""; // to make sure its excluded from Dashboard
si.elapsed_min = 0;
si.retry_cnt = 0xFF; // no startup retries
webScheduler.scheduleItems.push_back(si);
webScheduler.scheduleItems.push_back(si);
already_added = true;
// test 2
si = ScheduleItem();
si.active = false;
si.flags = 1;
si.time = "13:00";
si.cmd = "system/message";
si.value = "20";
si.name = ""; // to make sure its excluded from Dashboard
si.elapsed_min = 0;
si.retry_cnt = 0xFF; // no startup retries
return StateUpdateResult::CHANGED; // persist the changes
});
}
webScheduler.scheduleItems.push_back(si);
// test shunting yard
std::string test_cmd = "system/message";
std::string test_value;
// should output 'locale is en'
test_value = "\"locale is \"system/settings/locale";
command("test", test_cmd.c_str(), compute(test_value).c_str());
// test with negative value
// should output 'rssi is -23'
test_value = "\"rssi is \"0+system/network/rssi";
command("test1", test_cmd.c_str(), compute(test_value).c_str());
// should output 'rssi is -23 dbm'
test_value = "\"rssi is \"(system/network/rssi)\" dBm\"";
command("test2", test_cmd.c_str(), compute(test_value).c_str());
test_value = "(custom/seltemp/value)";
command("test3", test_cmd.c_str(), compute(test_value).c_str());
test_value = "\"seltemp=\"(custom/seltemp/value)";
command("test4", test_cmd.c_str(), compute(test_value).c_str());
test_value = "(custom/seltemp)";
command("test5", test_cmd.c_str(), compute(test_value).c_str());
test_value = "boiler/flowtempoffset";
command("test7", test_cmd.c_str(), compute(test_value).c_str());
test_value = "(boiler/flowtempoffset/value)";
command("test8", test_cmd.c_str(), compute(test_value).c_str());
test_value = "(boiler/storagetemp1/value)";
command("test9", test_cmd.c_str(), compute(test_value).c_str());
// (14 - 40) * 2.8 + 5 = -67.8
test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5";
command("test10", test_cmd.c_str(), compute(test_value).c_str());
// test case conversion
test_value = "(thermostat/hc1/modetype == \"comfort\")";
command("test11a", test_cmd.c_str(), compute(test_value).c_str()); // should be 1 true
test_value = "(thermostat/hc1/modetype == \"Comfort\")";
command("test11b", test_cmd.c_str(), compute(test_value).c_str()); // should be 1 true
test_value = "(thermostat/hc1/modetype == \"unknown\")";
command("test11c", test_cmd.c_str(), compute(test_value).c_str()); // should be 0 false
// can't find entity, should fail
test_value = "(boiler/storagetemp/value1)";
command("test12", test_cmd.c_str(), compute(test_value).c_str());
// can't find attribute, should fail
test_value = "(boiler/storagetemp1/value1)";
command("test13", test_cmd.c_str(), compute(test_value).c_str());
// check when entity has no value, should pass
test_value = "(boiler/storagetemp2/value)";
command("test14", test_cmd.c_str(), compute(test_value).c_str());
// should pass
test_value = "(boiler/storagetemp1/value)";
command("test15", test_cmd.c_str(), compute(test_value).c_str());
// test HTTP POST to call HA script
// test_cmd = "{\"method\":\"POST\",\"url\":\"http://192.168.1.42:8123/api/services/script/test_notify2\", \"header\":{\"authorization\":\"Bearer "
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhMmNlYWI5NDgzMmI0ODE2YWQ2NzU4MjkzZDE2YWMxZSIsImlhdCI6MTcyMTM5MTI0NCwiZXhwIjoyMDM2NzUxMjQ0fQ."
// "S5sago1tEI6lNhrDCO0dM_WsVQHkD_laAjcks8tWAqo\"}}";
// command("test99", test_cmd.c_str(), "");
return StateUpdateResult::CHANGED; // persist the changes
});
}
#endif

View File

@@ -87,7 +87,7 @@ class WebSchedulerService : public StatefulService<WebScheduler> {
bool onChange(const char * cmd);
#if defined(EMSESP_TEST)
void test();
void load_test_data();
#endif
// make all functions public so we can test in the debug and standalone mode

View File

@@ -115,7 +115,7 @@ void WebStatusService::systemStatus(AsyncWebServerRequest * request) {
root["max_alloc_heap"] = EMSESP::system_.getMaxAllocMem();
root["arduino_version"] = ARDUINO_VERSION;
root["sdk_version"] = ESP.getSdkVersion();
root["partition"] = esp_ota_get_running_partition()->label; // active partition
root["partition"] = (const char *)esp_ota_get_running_partition()->label; // active partition
root["flash_chip_size"] = ESP.getFlashChipSize() / 1024;
root["flash_chip_speed"] = ESP.getFlashChipSpeed();
root["app_used"] = EMSESP::system_.appUsed();

View File

@@ -4,7 +4,7 @@
# The response will be shown in the right panel
# @host = http://ems-esp.local
@host = http://192.168.1.225
@host = http://192.168.1.223
@host_dev = http://10.10.10.175
@host_standalone = http://localhost:3080
@host_standalone2 = http://localhost:3082
@@ -89,11 +89,59 @@ Authorization: Bearer {{token}}
GET {{host}}/api/custom/test_custom
#
# Test on dev
# Tests on dev
#
###
POST {{host_dev}}/api/system/message
Content-Type: application/json
Authorization: Bearer {{token}}
{
"value" : "'hello world'"
}
###
POST {{host_dev}}/api/system/message
Content-Type: application/json
Authorization: Bearer {{token}}
{
"value" : "system/settings/locale"
}
###
POST {{host_dev}}/api/system/message
Content-Type: application/json
Authorization: Bearer {{token}}
{
"value" : "custom/test_custom"
}
###
POST {{host_dev}}/api/thermostat/seltemp
Content-Type: application/json
Authorization: Bearer {{token}}
{
"value" : 21.0
}
###
GET {{host_dev}}/api/system/settings/locale/value
###
POST {{host_dev}}/api/system/settings/locale/value
###
GET {{host_dev}}/api/system/info
###

View File

@@ -4,7 +4,7 @@
# Command line test for the API
#
emsesp_url="http://192.168.1.225"
emsesp_url="http://192.168.1.223"
# get the token from the Security page. This is the token for the admin user, unless changed it'll always be the same
emsesp_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWV9.2bHpWya2C7Q12WjNUBD6_7N3RCD7CMl-EGhyQVzFdDg"
@@ -22,6 +22,15 @@ curl -X POST \
echo "\n"
# This example will use the message command to get the locale (en)
curl -X POST \
-H "Authorization: Bearer ${emsesp_token}" \
-H "Content-Type: application/json" \
-d '{"value":"system/settings/locale"}' \
${emsesp_url}/api/system/message
echo "\n"
# This example will export all values to a json file, including custom entities, sensors and schedules
curl -X POST \
-H "Authorization: Bearer ${emsesp_token}" \
@@ -34,7 +43,7 @@ echo "\n"
# This example is how to call a service in Home Assistant via the API
# Which can be added to an EMS-EPS schedule
ha_url="http://192.168.1.42:8123"
ha_url="http://192.168.1.86:8123"
ha_token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIwMzMyZjU1MjhlZmM0NGIyOTgyMjIxNThiODU1NDkyNSIsImlhdCI6MTcyMTMwNDg2NSwiZXhwIjoyMDM2NjY0ODY1fQ.Q-Y7E_i7clH3ff4Ma-OMmhZfbN7aMi_CahKwmoar"
curl -X POST \

View File

@@ -17,13 +17,12 @@
*/
#include <Arduino.h>
#include <unity.h>
#include <emsesp.h>
#include "ESPAsyncWebServer.h"
#include "web/WebAPIService.h"
#include "test_shuntingYard.hpp"
#include "test_shuntingYard.h"
using namespace emsesp;
@@ -280,6 +279,12 @@ void manual_test6() {
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/setvalue", data));
}
void manual_test7() {
auto expected_response = "[{}]"; // empty is good
char data[] = "{\"value\":9}";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_ram", data));
}
void run_manual_tests() {
RUN_TEST(manual_test1);
@@ -288,6 +293,7 @@ void run_manual_tests() {
RUN_TEST(manual_test4);
RUN_TEST(manual_test5);
RUN_TEST(manual_test6);
RUN_TEST(manual_test7);
}
const char * run_console_command(const char * command) {
@@ -362,14 +368,16 @@ void create_tests() {
// custom
capture("/api/custom");
capture("/api/custom/info");
capture("/api/custom/seltemp");
capture("/api/custom/test_seltemp");
capture("/api/custom/test_seltemp/value");
capture("/api/custom/test_custom");
// system
capture("/api/system");
capture("/api/system/info");
capture("/api/system/settings/locale");
capture("/api/system/fetch");
capture("api/system/network/values");
capture("/api/system/network/values");
// scheduler
capture("/api/scheduler");
@@ -389,7 +397,10 @@ void create_tests() {
capture("/api/analogsensor/test_analogsensor1");
capture("/api/analogsensor/test_analogsensor1/offset");
// these tests should all fail...
//
// these next tests should all fail...
//
capture("/api/boiler2");
capture("/api/boiler/bad/value");
capture("/api/boiler/comfort/valu");
@@ -405,8 +416,8 @@ void create_tests() {
capture("/api/scheduler/test_scheduler2/val2");
// custom
capture("/api/custom/seltemp2");
capture("/api/custom/seltemp/val");
capture("/api/custom/test_seltemp2");
capture("/api/custom/test_seltemp/val");
// temperaturesensor
capture("/api/temperaturesensor/test_sensor20");
@@ -437,15 +448,17 @@ int main() {
application.start(); // calls begin()
EMSESP::webCustomEntityService.test(); // custom entities
EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.test(); // add temperature sensors
EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions
// populate with data, like custom entities, fake temp sensors and scheduler items
EMSESP::webCustomEntityService.load_test_data(); // custom entities
EMSESP::webCustomizationService.load_test_data(); // set customizations - this will overwrite any settings in the FS
EMSESP::temperaturesensor_.load_test_data(); // add temperature sensors
EMSESP::webSchedulerService.load_test_data(); // run scheduler tests, and conditions
add_devices(); // add devices
#if defined(EMSESP_UNITY_CREATE)
create_tests();
return 0;
#endif
//

View File

@@ -1,23 +1,14 @@
// **************************************************************************************************
//
// Compile with -DEMSESP_UNITY_CREATE to generate the test functions, copy the output and paste below.
//
// TODO convert output to JSON and compare, showing differences
//
// You can also manually compare the differences using https://www.diffchecker.com/text-compare/
//
// **************************************************************************************************
// ---------- START - CUT HERE ----------
void test_1() {
auto expected_response =
"[{\"reset\":\"\",\"heatingoff\":\"off\",\"heatingactive\":\"off\",\"tapwateractive\":\"on\",\"selflowtemp\":0,\"curflowtemp\":60.2,\"rettemp\":48.1,"
"\"syspress\":1.4,\"burngas\":\"on\",\"burngas2\":\"off\",\"flamecurr\":37.4,\"fanwork\":\"on\",\"ignwork\":\"off\",\"oilpreheat\":\"off\","
"\"heatingpump\":\"on\",\"selburnpow\":115,\"curburnpow\":61,\"ubauptime\":3940268,\"servicecode\":\"=H\",\"servicecodenumber\":201,\"nompower\":0,"
"\"nrgtotal\":0.0,\"nrgheat\":0.0,\"dhw\":{\"seltemp\":52,\"comfort\":\"hot\",\"flowtempoffset\":40,\"circpump\":\"off\",\"chargetype\":\"3-way "
"valve\",\"hyston\":-5,\"hystoff\":0,\"disinfectiontemp\":70,\"circmode\":\"off\",\"circ\":\"off\",\"storagetemp1\":53.8,\"activated\":\"on\","
"\"3wayvalve\":\"on\",\"nrg\":0.0}}]";
auto expected_response = "[{\"reset\":\"\",\"heatingoff\":\"off\",\"heatingactive\":\"off\",\"tapwateractive\":\"on\",\"selflowtemp\":0,\"curflowtemp\":60."
"2,\"rettemp\":48.1,\"syspress\":1.4,\"burngas\":\"on\",\"burngas2\":\"off\",\"flamecurr\":37.4,\"fanwork\":\"on\",\"ignwork\":"
"\"off\",\"oilpreheat\":\"off\",\"heatingpump\":\"on\",\"selburnpow\":115,\"curburnpow\":61,\"ubauptime\":3940268,\"servicecode\":"
"\"=H\",\"servicecodenumber\":201,\"nompower\":0,\"nrgtotal\":0.0,\"nrgheat\":0.0,\"dhw\":{\"seltemp\":52,\"comfort\":\"hot\","
"\"flowtempoffset\":40,\"chargeoptimization\":\"off\",\"circpump\":\"off\",\"chargetype\":\"3-way "
"valve\",\"hyston\":-5,\"disinfectiontemp\":70,\"circmode\":\"off\",\"circ\":\"off\",\"storagetemp1\":53.8,\"activated\":\"on\","
"\"3wayvalve\":\"on\",\"chargepump\":\"off\",\"nrg\":0.0}}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler"));
}
@@ -46,13 +37,13 @@ void test_2() {
}
void test_3() {
auto expected_response =
"[{\"reset\":\"\",\"heatingoff\":\"off\",\"heatingactive\":\"off\",\"tapwateractive\":\"on\",\"selflowtemp\":0,\"curflowtemp\":60.2,\"rettemp\":48.1,"
"\"syspress\":1.4,\"burngas\":\"on\",\"burngas2\":\"off\",\"flamecurr\":37.4,\"fanwork\":\"on\",\"ignwork\":\"off\",\"oilpreheat\":\"off\","
"\"heatingpump\":\"on\",\"selburnpow\":115,\"curburnpow\":61,\"ubauptime\":3940268,\"servicecode\":\"=H\",\"servicecodenumber\":201,\"nompower\":0,"
"\"nrgtotal\":0.0,\"nrgheat\":0.0,\"dhw\":{\"seltemp\":52,\"comfort\":\"hot\",\"flowtempoffset\":40,\"circpump\":\"off\",\"chargetype\":\"3-way "
"valve\",\"hyston\":-5,\"hystoff\":0,\"disinfectiontemp\":70,\"circmode\":\"off\",\"circ\":\"off\",\"storagetemp1\":53.8,\"activated\":\"on\","
"\"3wayvalve\":\"on\",\"nrg\":0.0}}]";
auto expected_response = "[{\"reset\":\"\",\"heatingoff\":\"off\",\"heatingactive\":\"off\",\"tapwateractive\":\"on\",\"selflowtemp\":0,\"curflowtemp\":60."
"2,\"rettemp\":48.1,\"syspress\":1.4,\"burngas\":\"on\",\"burngas2\":\"off\",\"flamecurr\":37.4,\"fanwork\":\"on\",\"ignwork\":"
"\"off\",\"oilpreheat\":\"off\",\"heatingpump\":\"on\",\"selburnpow\":115,\"curburnpow\":61,\"ubauptime\":3940268,\"servicecode\":"
"\"=H\",\"servicecodenumber\":201,\"nompower\":0,\"nrgtotal\":0.0,\"nrgheat\":0.0,\"dhw\":{\"seltemp\":52,\"comfort\":\"hot\","
"\"flowtempoffset\":40,\"chargeoptimization\":\"off\",\"circpump\":\"off\",\"chargetype\":\"3-way "
"valve\",\"hyston\":-5,\"disinfectiontemp\":70,\"circmode\":\"off\",\"circ\":\"off\",\"storagetemp1\":53.8,\"activated\":\"on\","
"\"3wayvalve\":\"on\",\"chargepump\":\"off\",\"nrg\":0.0}}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/values"));
}
@@ -64,11 +55,12 @@ void test_4() {
"(flamecurr)\":37.4,\"fan (fanwork)\":\"on\",\"ignition (ignwork)\":\"off\",\"oil preheating (oilpreheat)\":\"off\",\"heating pump "
"(heatingpump)\":\"on\",\"burner selected max power (selburnpow)\":115,\"burner current power (curburnpow)\":61,\"total UBA operating time "
"(ubauptime)\":\"2736 days 7 hours 8 minutes\",\"service code (servicecode)\":\"=H\",\"service code number (servicecodenumber)\":201,\"dhw selected "
"temperature (seltemp)\":52,\"dhw comfort (comfort)\":\"hot\",\"dhw flow temperature offset (flowtempoffset)\":40,\"dhw circulation pump available "
"(circpump)\":\"off\",\"dhw charging type (chargetype)\":\"3-way valve\",\"dhw hysteresis on temperature (hyston)\":-5,\"dhw hysteresis off "
"temperature (hystoff)\":0,\"dhw disinfection temperature (disinfectiontemp)\":70,\"dhw circulation pump mode (circmode)\":\"off\",\"dhw circulation "
"active (circ)\":\"off\",\"dhw storage intern temperature (storagetemp1)\":53.8,\"dhw activated (activated)\":\"on\",\"dhw 3-way valve active "
"(3wayvalve)\":\"on\",\"nominal Power (nompower)\":0,\"total energy (nrgtotal)\":0.0,\"energy heating (nrgheat)\":0.0,\"dhw energy (nrg)\":0.0}]";
"temperature (seltemp)\":52,\"dhw comfort (comfort)\":\"hot\",\"dhw flow temperature offset (flowtempoffset)\":40,\"dhw charge optimization "
"(chargeoptimization)\":\"off\",\"dhw circulation pump available (circpump)\":\"off\",\"dhw charging type (chargetype)\":\"3-way valve\",\"dhw "
"hysteresis on temperature (hyston)\":-5,\"dhw disinfection temperature (disinfectiontemp)\":70,\"dhw circulation pump mode (circmode)\":\"off\",\"dhw "
"circulation active (circ)\":\"off\",\"dhw storage intern temperature (storagetemp1)\":53.8,\"dhw activated (activated)\":\"on\",\"dhw 3-way valve "
"active (3wayvalve)\":\"on\",\"dhw charge pump (chargepump)\":\"off\",\"nominal Power (nompower)\":0,\"total energy (nrgtotal)\":0.0,\"energy heating "
"(nrgheat)\":0.0,\"dhw energy (nrg)\":0.0}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/info"));
}
@@ -93,7 +85,6 @@ void test_8() {
auto expected_response = "[{\"name\":\"outdoortemp\",\"fullname\":\"outside "
"temperature\",\"circuit\":\"\",\"type\":\"number\",\"uom\":\"°C\",\"state_class\":\"measurement\",\"device_class\":"
"\"temperature\",\"readable\":true,\"writeable\":false,\"visible\":true}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/outdoortemp"));
}
@@ -140,22 +131,34 @@ void test_15() {
}
void test_16() {
auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":0.00,\"test_ram\":\"14\",\"seltemp\":\"14\"}]";
auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":70.00,\"test_ram\":\"14\",\"test_seltemp\":\"14\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom"));
}
void test_17() {
auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":0.00,\"test_ram\":\"14\",\"seltemp\":\"14\"}]";
auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":70.00,\"test_ram\":\"14\",\"test_seltemp\":\"14\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/info"));
}
void test_18() {
auto expected_response = "[{\"name\":\"seltemp\",\"fullname\":\"seltemp\",\"storage\":\"ram\",\"type\":\"number\",\"readable\":true,\"writeable\":true,"
"\"visible\":true,\"value\":\"14\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp"));
auto expected_response = "[{\"name\":\"test_seltemp\",\"fullname\":\"test_seltemp\",\"storage\":\"ram\",\"type\":\"number\",\"readable\":true,"
"\"writeable\":true,\"visible\":true,\"ent_cat\":\"diagnostic\",\"value\":\"14\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp"));
}
void test_19() {
auto expected_response = "[{\"api_data\":\"14\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp/value"));
}
void test_20() {
auto expected_response = "[{\"name\":\"test_custom\",\"fullname\":\"test_custom\",\"storage\":\"ems\",\"type\":\"number\",\"readable\":true,\"writeable\":"
"true,\"visible\":true,\"device_id\":\"0x08\",\"type_id\":\"0x18\",\"offset\":0,\"factor\":1,\"ent_cat\":\"diagnostic\",\"uom\":"
"\"°C\",\"state_class\":\"measurement\",\"device_class\":\"temperature\",\"value\":0.00}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_custom"));
}
void test_21() {
auto expected_response =
"[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / "
"Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false,"
@@ -170,10 +173,10 @@ void test_19() {
"\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11,"
"\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
"false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":"
"\"boiler\",\"name\":\"My Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":37,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x2E "
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
"Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":38,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x2E "
"0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15,"
"\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 "
"0x0168\"},{\"type\":\"temperaturesensor\",\"name\":\"temperaturesensor\",\"entities\":2},{\"type\":\"analogsensor\",\"name\":\"analogsensor\","
@@ -181,7 +184,7 @@ void test_19() {
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system"));
}
void test_20() {
void test_22() {
auto expected_response =
"[{\"system\":{\"version\":\"dev\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / "
"Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false,"
@@ -196,10 +199,10 @@ void test_20() {
"\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11,"
"\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":"
"false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true,"
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":"
"\"boiler\",\"name\":\"My Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":37,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x2E "
"\"maxWebLogBuffer\":25,\"modbusEnabled\":false,\"forceHeatingOff\":false,\"developerMode\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"My "
"Custom "
"Boiler\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":38,\"handlersReceived\":\"0x18\","
"\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0xC6 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x2E "
"0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15,"
"\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 "
"0x0168\"},{\"type\":\"temperaturesensor\",\"name\":\"temperaturesensor\",\"entities\":2},{\"type\":\"analogsensor\",\"name\":\"analogsensor\","
@@ -207,168 +210,168 @@ void test_20() {
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/info"));
}
void test_21() {
void test_23() {
auto expected_response =
"[{\"name\":\"locale\",\"circuit\":\"settings\",\"readable\":true,\"writeable\":false,\"visible\":true,\"value\":\"en\",\"type\":\"string\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale"));
}
void test_22() {
void test_24() {
auto expected_response = "[{}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/fetch"));
}
void test_23() {
void test_25() {
auto expected_response = "[{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":\"-23\",\"TxPowerSetting\":\"0\",\"staticIP\":\"false\","
"\"lowBandwidth\":\"false\",\"disableSleep\":\"true\",\"enableMDNS\":\"true\",\"enableCORS\":\"false\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("api/system/network/values"));
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/network/values"));
}
void test_24() {
void test_26() {
auto expected_response = "[{\"test_scheduler\":\"on\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler"));
}
void test_25() {
void test_27() {
auto expected_response = "[{\"test_scheduler\":\"on\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/info"));
}
void test_26() {
void test_28() {
auto expected_response = "[{\"name\":\"test_scheduler\",\"fullname\":\"test_scheduler\",\"type\":\"boolean\",\"value\":\"on\",\"time\":\"12:00\","
"\"command\":\"system/fetch\",\"cmd_data\":\"10\",\"readable\":true,\"writeable\":true,\"visible\":true}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler"));
}
void test_27() {
void test_29() {
auto expected_response = "[{\"test_tempsensor1\":12.3,\"test_tempsensor2\":45.6}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor"));
}
void test_28() {
void test_30() {
auto expected_response = "[{\"test_tempsensor1\":12.3,\"test_tempsensor2\":45.6}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/info"));
}
void test_29() {
void test_31() {
auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_tempsensor2\",\"fullname\":\"test_tempsensor2\",\"value\":45.6,\"type\":"
"\"number\",\"uom\":\"°C\",\"readable\":true,\"writeable\":false,\"visible\":true}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2"));
}
void test_30() {
void test_32() {
auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_tempsensor2\",\"fullname\":\"test_tempsensor2\",\"value\":45.6,\"type\":"
"\"number\",\"uom\":\"°C\",\"readable\":true,\"writeable\":false,\"visible\":true}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_1011"));
}
void test_31() {
void test_33() {
auto expected_response = "[{\"api_data\":\"45.6\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/value"));
}
void test_32() {
void test_34() {
auto expected_response = "[{\"test_analogsensor1\":0,\"test_analogsensor2\":1,\"test_analogsensor3\":0,\"test_analogsensor4\":0}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor"));
}
void test_33() {
void test_35() {
auto expected_response = "[{\"test_analogsensor1\":0,\"test_analogsensor2\":1,\"test_analogsensor3\":0,\"test_analogsensor4\":0}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/info"));
}
void test_34() {
void test_36() {
auto expected_response = "[{\"name\":\"test_analogsensor1\",\"fullname\":\"test_analogsensor1\",\"gpio\":36,\"type\":\"number\",\"analog\":\"adc\","
"\"value\":0,\"readable\":true,\"writeable\":false,\"visible\":true,\"offset\":0,\"factor\":0.1,\"uom\":\"mV\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1"));
}
void test_35() {
void test_37() {
auto expected_response = "[{\"api_data\":\"0\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/offset"));
}
void test_36() {
void test_38() {
auto expected_response = "[{\"message\":\"unknown device boiler2\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler2"));
}
void test_37() {
void test_39() {
auto expected_response = "[{}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/bad/value"));
}
void test_38() {
void test_40() {
auto expected_response = "[{\"message\":\"no attribute 'valu' in comfort\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/valu"));
}
void test_39() {
void test_41() {
auto expected_response = "[{\"message\":\"no entity 'settings' in system\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale2"));
}
void test_40() {
void test_42() {
auto expected_response = "[{\"message\":\"no entity 'settings2' in system\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2"));
}
void test_41() {
void test_43() {
auto expected_response = "[{\"message\":\"no entity 'settings2' in system\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2/locale2"));
}
void test_42() {
void test_44() {
auto expected_response = "[{\"message\":\"no entity 'test_scheduler2' in scheduler\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2"));
}
void test_43() {
void test_45() {
auto expected_response = "[{\"message\":\"no attribute 'val' in test_scheduler\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler/val"));
}
void test_44() {
void test_46() {
auto expected_response = "[{\"message\":\"no entity 'test_scheduler2' in scheduler\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2/val2"));
}
void test_45() {
auto expected_response = "[{\"message\":\"no entity 'seltemp2' in custom\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp2"));
}
void test_46() {
auto expected_response = "[{\"message\":\"no attribute 'val' in seltemp\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp/val"));
}
void test_47() {
auto expected_response = "[{\"message\":\"no entity 'test_seltemp2' in custom\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp2"));
}
void test_48() {
auto expected_response = "[{\"message\":\"Command test_seltemp failed (Error)\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/test_seltemp/val"));
}
void test_49() {
auto expected_response = "[{\"message\":\"no entity 'test_sensor20' in temperaturesensor\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor20"));
}
void test_48() {
void test_50() {
auto expected_response = "[{\"message\":\"no entity '0b_0c0d_0e0f_xxxx' in temperaturesensor\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_XXXX"));
}
void test_49() {
void test_51() {
auto expected_response = "[{\"message\":\"no attribute 'bad' in test_tempsensor2\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_tempsensor2/bad"));
}
void test_50() {
void test_52() {
auto expected_response = "[{\"message\":\"no attribute 'bad' in test_analogsensor1\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analogsensor1/bad"));
}
void test_51() {
void test_53() {
auto expected_response = "[{\"message\":\"no entity 'test_analog10' in analogsensor\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10"));
}
void test_52() {
void test_54() {
auto expected_response = "[{\"message\":\"no entity 'test_analog10' in analogsensor\"}]";
TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10/bad2"));
}
@@ -426,6 +429,8 @@ void run_tests() {
RUN_TEST(test_50);
RUN_TEST(test_51);
RUN_TEST(test_52);
RUN_TEST(test_53);
RUN_TEST(test_54);
}
// ---------- END - CUT HERE ----------
// ---------- END - CUT HERE ----------

View File

@@ -0,0 +1,136 @@
#include <Arduino.h>
#include <unity.h>
#include "core/shuntingYard.h"
void run_shuntingYard_test(const std::string & expected, const std::string & actual) {
TEST_ASSERT_EQUAL_STRING(expected.c_str(), compute(actual).c_str());
}
void shuntingYard_test1() {
run_shuntingYard_test("locale is en", "\"locale is \"system/settings/locale");
}
void shuntingYard_test2() {
run_shuntingYard_test("rssi is -23", "\"rssi is \"0+system/network/rssi");
}
void shuntingYard_test3() {
run_shuntingYard_test("rssi is -23 dBm", "\"rssi is \"(system/network/rssi)\" dBm\"");
}
void shuntingYard_test4() {
run_shuntingYard_test("14", "(custom/test_seltemp/value)");
}
void shuntingYard_test5() {
run_shuntingYard_test("seltemp=14", "\"seltemp=\"(custom/test_seltemp/value)");
}
void shuntingYard_test6() {
run_shuntingYard_test("14", "custom/test_seltemp");
}
void shuntingYard_test7() {
run_shuntingYard_test("40", "boiler/flowtempoffset");
}
void shuntingYard_test8() {
run_shuntingYard_test("40", "(boiler/flowtempoffset/value)");
}
void shuntingYard_test9() {
run_shuntingYard_test("53.8", "(boiler/storagetemp1/value)");
}
void shuntingYard_test10() {
run_shuntingYard_test("-67.8", "(custom/test_seltemp - boiler/flowtempoffset) * 2.8 + 5");
}
void shuntingYard_test11() {
run_shuntingYard_test("4", "1 > 2 ? 3 : 4");
}
void shuntingYard_test12() {
run_shuntingYard_test("3", "1 < 2 ? 3 : 4");
}
void shuntingYard_test13() {
run_shuntingYard_test("5", "1<2?(3<4?5:6):7");
}
void shuntingYard_test14() {
run_shuntingYard_test("7", "1>2?(3<4?5:6):7");
}
void shuntingYard_test15() {
run_shuntingYard_test("6", "1<2?(3>4?5:6):7");
}
void shuntingYard_test16() {
run_shuntingYard_test("3", "1<2?3:(4<5?6:7)");
}
void shuntingYard_test17() {
run_shuntingYard_test("6", "1>2?3:(4<5?6:7)");
}
void shuntingYard_test18() {
run_shuntingYard_test("7", "1>2?3:(4>5?6:7)");
}
void shuntingYard_test19() {
run_shuntingYard_test("44", "(1>2?3:4)+(10>20?30:40)");
}
void shuntingYard_test20() {
run_shuntingYard_test("8", "1<2 ? 3>4 ? 5 : 6<7 ? 8 : 9 : 10");
}
void shuntingYard_test21() {
run_shuntingYard_test("", "boiler/storagetemp2 == \"\"");
}
void shuntingYard_test22() {
run_shuntingYard_test("", "boiler/storagetemp2 == ''");
}
void shuntingYard_test23() {
run_shuntingYard_test("9", "custom/test_ram");
}
void shuntingYard_test24() {
run_shuntingYard_test("hello world!", "\"hello world!\"");
}
void shuntingYard_test25() {
run_shuntingYard_test("hello world!", "'hello world!'");
}
void run_shuntingYard_tests() {
RUN_TEST(shuntingYard_test1);
RUN_TEST(shuntingYard_test2);
RUN_TEST(shuntingYard_test3);
RUN_TEST(shuntingYard_test4);
RUN_TEST(shuntingYard_test5);
RUN_TEST(shuntingYard_test6);
RUN_TEST(shuntingYard_test7);
RUN_TEST(shuntingYard_test8);
RUN_TEST(shuntingYard_test9);
RUN_TEST(shuntingYard_test10);
RUN_TEST(shuntingYard_test11);
RUN_TEST(shuntingYard_test12);
RUN_TEST(shuntingYard_test13);
RUN_TEST(shuntingYard_test14);
RUN_TEST(shuntingYard_test15);
RUN_TEST(shuntingYard_test16);
RUN_TEST(shuntingYard_test17);
RUN_TEST(shuntingYard_test18);
RUN_TEST(shuntingYard_test19);
RUN_TEST(shuntingYard_test20);
RUN_TEST(shuntingYard_test21);
RUN_TEST(shuntingYard_test22);
RUN_TEST(shuntingYard_test23);
RUN_TEST(shuntingYard_test24);
RUN_TEST(shuntingYard_test25);
}

View File

@@ -1,157 +0,0 @@
#include <Arduino.h>
#include <unity.h>
#include <HTTPClient.h>
#include "core/shuntingYard.hpp"
void shuntingYard_test1() {
std::string expected_result = "locale is en";
std::string test_value = "\"locale is \"system/settings/locale";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test2() {
// test with negative value
std::string expected_result = "rssi is -23";
std::string test_value = "\"rssi is \"0+system/network/rssi";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test3() {
std::string expected_result = "rssi is -23 dBm";
std::string test_value = "\"rssi is \"(system/network/rssi)\" dBm\"";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test4() {
std::string expected_result = "14";
std::string test_value = "(custom/seltemp/value)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test5() {
std::string expected_result = "seltemp=14";
std::string test_value = "\"seltemp=\"(custom/seltemp/value)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test6() {
std::string expected_result = "14";
std::string test_value = "(custom/seltemp)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test7() {
std::string expected_result = "40";
std::string test_value = "boiler/flowtempoffset";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test8() {
std::string expected_result = "40";
std::string test_value = "(boiler/flowtempoffset/value)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test9() {
std::string expected_result = "53.8";
std::string test_value = "(boiler/storagetemp1/value)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test10() {
// (14 - 40) * 2.8 + 5 = -67.8
std::string expected_result = "-67.8";
std::string test_value = "(custom/seltemp - boiler/flowtempoffset) * 2.8 + 5";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test11() {
std::string expected_result = "4";
std::string test_value = "1 > 2 ? 3 : 4";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test12() {
std::string expected_result = "3";
std::string test_value = "1 < 2 ? 3 : 4";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test13() {
std::string expected_result = "5";
std::string test_value = "1<2?(3<4?5:6):7";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test14() {
std::string expected_result = "7";
std::string test_value = "1>2?(3<4?5:6):7";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test15() {
std::string expected_result = "6";
std::string test_value = "1<2?(3>4?5:6):7";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test16() {
std::string expected_result = "3";
std::string test_value = "1<2?3:(4<5?6:7)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test17() {
std::string expected_result = "6";
std::string test_value = "1>2?3:(4<5?6:7)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test18() {
std::string expected_result = "7";
std::string test_value = "1>2?3:(4>5?6:7)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test19() {
std::string expected_result = "44";
std::string test_value = "(1>2?3:4)+(10>20?30:40)";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test20() {
std::string expected_result = "8";
std::string test_value = "1<2 ? 3>4 ? 5 : 6<7 ? 8 : 9 : 10";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void shuntingYard_test21() {
std::string expected_result = "1";
std::string test_value = "boiler/storagetemp2 == \"\"";
TEST_ASSERT_EQUAL_STRING(expected_result.c_str(), compute(test_value).c_str());
}
void run_shuntingYard_tests() {
RUN_TEST(shuntingYard_test1);
RUN_TEST(shuntingYard_test2);
RUN_TEST(shuntingYard_test3);
RUN_TEST(shuntingYard_test4);
RUN_TEST(shuntingYard_test5);
RUN_TEST(shuntingYard_test6);
RUN_TEST(shuntingYard_test7);
RUN_TEST(shuntingYard_test8);
RUN_TEST(shuntingYard_test9);
RUN_TEST(shuntingYard_test10);
RUN_TEST(shuntingYard_test11);
RUN_TEST(shuntingYard_test12);
RUN_TEST(shuntingYard_test13);
RUN_TEST(shuntingYard_test14);
RUN_TEST(shuntingYard_test15);
RUN_TEST(shuntingYard_test16);
RUN_TEST(shuntingYard_test17);
RUN_TEST(shuntingYard_test18);
RUN_TEST(shuntingYard_test19);
RUN_TEST(shuntingYard_test20);
RUN_TEST(shuntingYard_test21);
}