Compare commits

...
Author SHA1 Message Date
ESPEasy release bot e36a9f163e automaticly updated release notes for mega-20180123 2018-01-23 05:00:10 +01:00
Edwin Eefting 17f0f6637a Merge branch 'v2.0' into mega 2018-01-23 04:58:52 +01:00
Edwin Eefting bc91939757 request mainpage when checking if reboot is complete 2018-01-23 04:57:20 +01:00
Edwin Eefting eb743f95cf added easy reboot warning and function to work around several issues 2018-01-23 04:49:16 +01:00
Edwin Eefting 0ec770fc51 Merge branch 'v2.0' into mega 2018-01-23 02:43:07 +01:00
Edwin Eefting ed4cdc3d90 clear custom task and controller settings when selecting a different device or protocol. replaces #762 . fixes #721 2018-01-23 02:37:02 +01:00
Edwin Eefting 9da5f465d1 Merge branch 'v2.0' into mega 2018-01-23 01:02:12 +01:00
Edwin Eefting 8e72ace3b5 test suite: espcli can now do stuff for all configured nodes 2018-01-23 00:50:00 +01:00
s0170071andDatuX 5230a4fe4f CRC on settings and security settings and progmem (#720)
* preparations for lwip2

* Add CRC32 to settings

* Add CRC to progmem

* Add crc of program memory to stettings and security settings

.. so that there is a warning if the settings are used by another binary than they were written with.

* Moved to md5

* Flash included, moved to MD5 now
2018-01-22 23:58:43 +01:00
Edwin Eefting b236887718 Merge branch 'v2.0' into mega 2018-01-22 23:54:55 +01:00
Gijs NoorlanderandDatuX 2527cbe097 Central function for system variable replacement (#757)
* Central function for system variable replacement

Related to:
- #698 => Not complete
- #709 => fixed
- #751 => fixed

Redesign of PR #485 (in mega branch)

One note: no sensor values can be used in controllers. Is this what we want, or should we call parseTemplate everywhere parseSystemVariables is being used?
Also not all controllers apparently allow system variables. Is this also correct?

* [issue 757] Add %lcltime% to display date + time

As requested by @PsychoAndi at PR #757

* [issue 757] Add local time to JSON output

As suggested by @PsychoAndi
2018-01-22 23:38:14 +01:00
Edwin Eefting cbca4c3efa Merge branch 'v2.0' into mega 2018-01-22 23:35:45 +01:00
Edwin Eefting 5b0e9d934c test suite: now possible to run all tests at once. fixes 2018-01-22 23:32:46 +01:00
ESPEasy release bot 19429e8507 automaticly updated release notes for v2.0-20180122 2018-01-22 05:00:19 +01:00
ESPEasy release bot a67f92176f automaticly updated release notes for mega-20180122 2018-01-22 05:00:08 +01:00
Edwin Eefting 3922827fad Merged v2.0 changes and fixed some regressions 2018-01-22 01:43:42 +01:00
Gijs NoorlanderandDatuX 0c637da19f [#732] Only process P029 MQTT helper when enabled (#743)
See #732
Also did some small optimizations for when no Domoticz MQTT controller enabled and/or task enabled.
2018-01-21 17:10:08 +01:00
Gijs NoorlanderandDatuX 48f168da9d [issue #745] Set IPaddress to 0.0.0.0 when read from empty string (#754)
Also str2ip now uses IPAddress.fromString()
2018-01-21 17:08:15 +01:00
ESPEasy release bot 3357eb287c automaticly updated release notes for v2.0-20180121 2018-01-21 05:00:13 +01:00
Edwin Eefting 5da1c54b70 test suite: fixes. testing boot gpio states 2018-01-21 04:28:12 +01:00
Edwin Eefting c5bbdb401a test suite: lots of refactoring and fixing 2018-01-21 03:23:22 +01:00
Edwin Eefting f69b84d24b test suite: fixed issues, started working on nodo 2018-01-21 01:53:22 +01:00
Edwin Eefting 1250e09fef cleaned up nodo logging. fixed domotics SENSOR_TYPE_WIND inconsistency 2018-01-21 01:52:36 +01:00
Edwin Eefting fbf22ba7af test suite: generelized controller testing in test005 2018-01-20 21:49:24 +01:00
ESPEasy release bot 5c26d50227 automaticly updated release notes for v2.0-20180120 2018-01-20 05:00:12 +01:00
Edwin Eefting 47be037be9 Merge branch 'v2.0' of github.com:letscontrolit/ESPEasy into v2.0 2018-01-19 05:45:10 +01:00
Edwin Eefting bc4f7d390c test suite: started on testcase to test all sensor types via the dummy plugin 2018-01-19 05:44:57 +01:00
Edwin Eefting 9fe64834f9 added QUAD to c002. fixed getBearing and SENSOR_TYPE_WIND. 2018-01-19 05:43:50 +01:00
ESPEasy release bot 4d943c2386 automaticly updated release notes for v2.0-20180119 2018-01-19 05:00:18 +01:00
ESPEasy release bot 4b298d6e31 automaticly updated release notes for mega-20180119 2018-01-19 05:00:07 +01:00
jctualandDatuX c0c65364e5 DHT plugin update (#736)
Modification of DHT plugin to use Sonoff am2301 and si7021 sensors.
Also correct some NAN readings.
2018-01-19 02:43:22 +01:00
Edwin Eefting 51b76682cf test suite: added dht22 2018-01-19 02:35:38 +01:00
Edwin Eefting 5f89698308 Merge branch 'v2.0' into mega 2018-01-19 01:30:40 +01:00
Edwin Eefting 400ee82686 test suite domoticz mqtt + found few fixmes 2018-01-19 01:28:14 +01:00
ESPEasy release bot 485f97bdc2 automaticly updated release notes for v2.0-20180118 2018-01-18 05:00:19 +01:00
ESPEasy release bot 5821377b6c automaticly updated release notes for mega-20180118 2018-01-18 05:00:08 +01:00
Edwin Eefting e6e3c1ddee refactoring test suite controller stuff 2018-01-18 03:37:25 +01:00
Edwin Eefting a6ea35ef9c Merge branch 'v2.0' into mega 2018-01-18 02:15:01 +01:00
Edwin Eefting fe9a579dc3 added fixme 2018-01-18 02:03:54 +01:00
Gijs NoorlanderandDatuX 017aad051e [issue 728] Proper reconnect for MQTT controllers (#737)
See #728
Also looked into the very strange configuration settings lookup for MQTT settings. The way it was, the settings for the first MQTT controller were used even when the actual MQTT controller was inactive or using different settings.

Now the controller selected in the the plugin is being used and when no plugin setting is available (and also for P037 MQTT import) the first active MQTT controller will be used.
2018-01-18 01:04:55 +01:00
Gijs NoorlanderandDatuX 8a30659015 [issue 733] Fix for negative values on MQTT import (#735)
See #733.
The '-' was parsed, but not copied to the tmp char array to be converted.
2018-01-18 00:44:08 +01:00
ESPEasy release bot ed7f597b3d automaticly updated release notes for mega-20180117 2018-01-17 05:00:08 +01:00
Edwin Eefting 6700030d7c Merge branch 'mega' of github.com:letscontrolit/ESPEasy into mega 2018-01-17 00:16:09 +01:00
Edwin Eefting f45c7650de fix #719 (initial ds18b20 value after power on 85 degrees) 2018-01-17 00:15:59 +01:00
papperoneandDatuX a36ee65865 merge with main branch (#648)
* removed note for 74HC595

74HC595 pin reminder not needed as this chip won't be supported anymore

* deleted GPIO notes referring to 75HC595 not supported
2018-01-16 20:20:52 +01:00
ESPEasy release bot b61c638f0a automaticly updated release notes for v2.0-20180116 2018-01-16 05:00:21 +01:00
Edwin Eefting 09e5d6abcd now supports powercycle to test for bugs like #719 2018-01-16 04:15:48 +01:00
Edwin Eefting 2aafbb57a8 made test resumable and importable 2018-01-16 03:41:29 +01:00
37 changed files with 1968 additions and 777 deletions
+98
View File
@@ -1,3 +1,101 @@
-------------------------------------------------
Changes in release mega-20180123 (since mega-20180122)
-------------------------------------------------
Release date: Tue Jan 23 05:00:10 CET 2018
ESPEasy release bot (1):
automaticly updated release notes for v2.0-20180122
Edwin Eefting (5):
test suite: now possible to run all tests at once. fixes
test suite: espcli can now do stuff for all configured nodes
clear custom task and controller settings when selecting a different device or protocol. replaces #762 . fixes #721
added easy reboot warning and function to work around several issues
request mainpage when checking if reboot is complete
Gijs Noorlander (1):
Central function for system variable replacement (#757)
s0170071 (1):
CRC on settings and security settings and progmem (#720)
-------------------------------------------------
Changes in release mega-20180122 (since mega-20180119)
-------------------------------------------------
Release date: Mon Jan 22 05:00:08 CET 2018
ESPEasy release bot (3):
automaticly updated release notes for v2.0-20180119
automaticly updated release notes for v2.0-20180120
automaticly updated release notes for v2.0-20180121
Edwin Eefting (8):
test suite: added dht22
added QUAD to c002. fixed getBearing and SENSOR_TYPE_WIND.
test suite: started on testcase to test all sensor types via the dummy plugin
test suite: generelized controller testing in test005
cleaned up nodo logging. fixed domotics SENSOR_TYPE_WIND inconsistency
test suite: fixed issues, started working on nodo
test suite: lots of refactoring and fixing
test suite: fixes. testing boot gpio states
Gijs Noorlander (2):
[issue #745] Set IPaddress to 0.0.0.0 when read from empty string (#754)
[#732] Only process P029 MQTT helper when enabled (#743)
-------------------------------------------------
Changes in release mega-20180119 (since mega-20180118)
-------------------------------------------------
Release date: Fri Jan 19 05:00:07 CET 2018
ESPEasy release bot (1):
automaticly updated release notes for v2.0-20180118
Edwin Eefting (2):
refactoring test suite controller stuff
test suite domoticz mqtt + found few fixmes
jctual (1):
DHT plugin update (#736)
-------------------------------------------------
Changes in release mega-20180118 (since mega-20180117)
-------------------------------------------------
Release date: Thu Jan 18 05:00:07 CET 2018
ESPEasy release bot (1):
automaticly updated release notes for v2.0-20180116
Edwin Eefting (3):
made test resumable and importable
now supports powercycle to test for bugs like #719
added fixme
Gijs Noorlander (2):
[issue 733] Fix for negative values on MQTT import (#735)
[issue 728] Proper reconnect for MQTT controllers (#737)
-------------------------------------------------
Changes in release mega-20180117 (since mega-20180116)
-------------------------------------------------
Release date: Wed Jan 17 05:00:08 CET 2018
Edwin Eefting (1):
fix #719 (initial ds18b20 value after power on 85 degrees)
papperone (1):
merge with main branch (#648)
-------------------------------------------------
Changes in release mega-20180116 (since mega-20180114)
-------------------------------------------------
+28 -17
View File
@@ -213,6 +213,14 @@ void ExecuteCommand(byte source, const char *Line)
taskClear(Par1 - 1, true);
}
//quickly clear all tasks, without saving (used by test suite)
if (strcasecmp_P(Command, PSTR("TaskClearAll")) == 0)
{
success = true;
for (byte t=0; t<TASKS_MAX; t++)
taskClear(t, false);
}
if (strcasecmp_P(Command, PSTR("wdconfig")) == 0)
{
success = true;
@@ -366,15 +374,19 @@ void ExecuteCommand(byte source, const char *Line)
}
if (strcasecmp_P(Command, PSTR("Publish")) == 0 && WiFi.status() == WL_CONNECTED)
{
success = true;
String event = Line;
event = event.substring(8);
int index = event.indexOf(',');
if (index > 0)
{
String topic = event.substring(0, index);
String value = event.substring(index + 1);
MQTTclient.publish(topic.c_str(), value.c_str(), Settings.MQTTRetainFlag);
// ToDo TD-er: Not sure about this function, but at least it sends to an existing MQTTclient
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
success = true;
String event = Line;
event = event.substring(8);
int index = event.indexOf(',');
if (index > 0)
{
String topic = event.substring(0, index);
String value = event.substring(index + 1);
MQTTpublish(enabledMqttController, topic.c_str(), value.c_str(), Settings.MQTTRetainFlag);
}
}
}
@@ -386,14 +398,12 @@ void ExecuteCommand(byte source, const char *Line)
String port = parseString(strLine, 3);
int msgpos = getParamStartPos(strLine, 4);
String message = strLine.substring(msgpos);
byte ipaddress[4];
str2ip((char*)ip.c_str(), ipaddress);
IPAddress UDP_IP(ipaddress[0], ipaddress[1], ipaddress[2], ipaddress[3]);
portUDP.beginPacket(UDP_IP, port.toInt());
#if defined(ESP8266)
IPAddress UDP_IP;
if(UDP_IP.fromString(ip)) {
portUDP.beginPacket(UDP_IP, port.toInt());
portUDP.write(message.c_str(), message.length());
#endif
portUDP.endPacket();
portUDP.endPacket();
}
}
if (strcasecmp_P(Command, PSTR("SendToHTTP")) == 0 && WiFi.status() == WL_CONNECTED)
@@ -555,9 +565,10 @@ void ExecuteCommand(byte source, const char *Line)
if (strcasecmp_P(Command, PSTR("IP")) == 0)
{
success = true;
if (GetArgv(Line, TmpStr1, 2))
if (GetArgv(Line, TmpStr1, 2)) {
if (!str2ip(TmpStr1, Settings.IP))
Serial.println("?");
}
}
if (strcasecmp_P(Command, PSTR("Settings")) == 0)
+30 -16
View File
@@ -80,8 +80,12 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
char c_payload[384];
statusLED(true);
if (length>sizeof(c_payload)-1)
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController < 0) {
addLog(LOG_LEVEL_ERROR, F("MQTT : No enabled MQTT controller"));
return;
}
if ((length + 1) > sizeof(c_payload))
{
addLog(LOG_LEVEL_ERROR, F("MQTT : Ignored too big message"));
return;
@@ -108,7 +112,7 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
struct EventStruct TempEvent;
TempEvent.String1 = c_topic;
TempEvent.String2 = c_payload;
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[0]);
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[enabledMqttController]);
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_RECV, &TempEvent, dummyString);
}
@@ -116,11 +120,11 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) {
/*********************************************************************************************\
* Connect to MQTT message broker
\*********************************************************************************************/
void MQTTConnect()
void MQTTConnect(int controller_idx)
{
if (WiFi.status() != WL_CONNECTED) return;
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(0, (byte*)&ControllerSettings, sizeof(ControllerSettings)); // todo index is now fixed to 0
LoadControllerSettings(controller_idx, (byte*)&ControllerSettings, sizeof(ControllerSettings));
if (ControllerSettings.UseDNS) {
MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
@@ -145,8 +149,8 @@ void MQTTConnect()
String log = "";
boolean MQTTresult = false;
if ((SecuritySettings.ControllerUser[0] != 0) && (SecuritySettings.ControllerPassword[0] != 0))
MQTTresult = MQTTclient.connect(clientid.c_str(), SecuritySettings.ControllerUser[0], SecuritySettings.ControllerPassword[0], LWTTopic.c_str(), 0, 1, "Connection Lost");
if ((SecuritySettings.ControllerUser[controller_idx] != 0) && (SecuritySettings.ControllerPassword[controller_idx] != 0))
MQTTresult = MQTTclient.connect(clientid.c_str(), SecuritySettings.ControllerUser[controller_idx], SecuritySettings.ControllerPassword[controller_idx], LWTTopic.c_str(), 0, 0, "Connection Lost");
else
MQTTresult = MQTTclient.connect(clientid.c_str(), LWTTopic.c_str(), 0, 1, "Connection Lost");
@@ -184,9 +188,9 @@ void MQTTConnect()
/*********************************************************************************************\
* Check connection MQTT message broker
\*********************************************************************************************/
void MQTTCheck()
void MQTTCheck(int controller_idx)
{
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[0]);
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[controller_idx]);
if (Protocol[ProtocolIndex].usesMQTT)
{
if (!MQTTclient.connected() || WiFi.status() != WL_CONNECTED)
@@ -196,7 +200,7 @@ void MQTTCheck()
MQTTclient.disconnect();
if (WiFi.status() == WL_CONNECTED) {
delay(1000);
MQTTConnect();
MQTTConnect(controller_idx);
}
}
else if (connectionFailures)
@@ -226,6 +230,14 @@ void SendStatus(byte source, String status)
}
}
boolean MQTTpublish(int controller_idx, const char* topic, const char* payload, boolean retained)
{
if (MQTTclient.publish(topic, payload, retained))
return true;
addLog(LOG_LEVEL_DEBUG, F("MQTT : publish failed"));
MQTTConnect(controller_idx);
return false;
}
/*********************************************************************************************\
* Send status info back to channel where request came from
@@ -233,10 +245,12 @@ void SendStatus(byte source, String status)
void MQTTStatus(String& status)
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(0, (byte*)&ControllerSettings, sizeof(ControllerSettings)); // todo index is now fixed to 0
String pubname = ControllerSettings.Subscribe;
pubname.replace(F("/#"), F("/status"));
pubname.replace(F("%sysname%"), Settings.Name);
MQTTclient.publish(pubname.c_str(), status.c_str(),Settings.MQTTRetainFlag);
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
LoadControllerSettings(enabledMqttController, (byte*)&ControllerSettings, sizeof(ControllerSettings));
String pubname = ControllerSettings.Subscribe;
pubname.replace(F("/#"), F("/status"));
pubname.replace(F("%sysname%"), Settings.Name);
MQTTpublish(enabledMqttController, pubname.c_str(), status.c_str(),Settings.MQTTRetainFlag);
}
}
+47 -9
View File
@@ -416,6 +416,18 @@ WiFiUDP portUDP;
struct SecurityStruct
{
SecurityStruct() {
memset(WifiSSID, 0, sizeof(WifiSSID));
memset(WifiKey, 0, sizeof(WifiKey));
memset(WifiSSID2, 0, sizeof(WifiSSID2));
memset(WifiKey2, 0, sizeof(WifiKey2));
memset(WifiAPKey, 0, sizeof(WifiAPKey));
for (byte i = 0; i < CONTROLLER_MAX; ++i) {
memset(ControllerUser[i], 0, sizeof(ControllerUser[i]));
memset(ControllerPassword[i], 0, sizeof(ControllerPassword[i]));
}
memset(Password, 0, sizeof(Password));
}
char WifiSSID[32];
char WifiKey[64];
char WifiSSID2[32];
@@ -427,7 +439,9 @@ struct SecurityStruct
byte AllowedIPrangeLow[4]; // TD-er: Use these
byte AllowedIPrangeHigh[4];
byte IPblockLevel;
//its safe to extend this struct, up to 4096 bytes, default values in config are 0
//its safe to extend this struct, up to 4096 bytes, default values in config are 0. Make sure crc is last
uint8_t ProgmemMd5[16]; // crc of the binary that last saved the struct to file.
uint8_t md5[16];
} SecuritySettings;
struct SettingsStruct
@@ -546,6 +560,9 @@ struct SettingsStruct
//its safe to extend this struct, up to several bytes, default values in config are 0
//look in misc.ino how config.dat is used because also other stuff is stored in it at different offsets.
//TODO: document config.dat somewhere here
// make sure crc is the last value in the struct
uint8_t ProgmemMd5[16]; // crc of the binary that last saved the struct to file.
uint8_t md5[16];
} Settings;
struct ControllerSettingsStruct
@@ -845,6 +862,9 @@ String eventBuffer = "";
uint32_t lowestRAM = 0;
String lowestRAMfunction = "";
uint8_t thisBinaryMd5[16]={0};
bool shouldReboot=false;
/*********************************************************************************************\
* SETUP
@@ -860,7 +880,7 @@ void setup()
Serial.begin(115200);
// Serial.print("\n\n\nBOOOTTT\n\n\n");
initLog();
@@ -912,8 +932,8 @@ void setup()
addLog(LOG_LEVEL_INFO, log);
fileSystemCheck();
progMemMD5check();
LoadSettings();
if (strcasecmp(SecuritySettings.WifiSSID, "ssid") == 0)
wifiSetup = true;
@@ -1002,9 +1022,11 @@ void setup()
portUDP.begin(Settings.UDPPort);
// Setup MQTT Client
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[0]);
if (Protocol[ProtocolIndex].usesMQTT && Settings.ControllerEnabled[0])
MQTTConnect();
// ToDo TD-er: Controller index is forced to the first enabled MQTT controller.
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
MQTTConnect(enabledMqttController);
}
sendSysInfoUDP(3);
@@ -1031,6 +1053,16 @@ void setup()
}
int firstEnabledMQTTController() {
for (byte i = 0; i < CONTROLLER_MAX; ++i) {
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[i]);
if (Protocol[ProtocolIndex].usesMQTT && Settings.ControllerEnabled[i]) {
return i;
}
}
return -1;
}
/*********************************************************************************************\
* MAIN LOOP
@@ -1074,8 +1106,10 @@ void loop()
}
//dont do this in backgroundtasks(), otherwise causes crashes. (https://github.com/letscontrolit/ESPEasy/issues/683)
if(Settings.ControllerEnabled[0])
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
MQTTclient.loop();
}
backgroundtasks();
@@ -1217,8 +1251,12 @@ void runEach30Seconds()
addLog(LOG_LEVEL_INFO, log);
sendSysInfoUDP(1);
refreshNodeList();
if(Settings.ControllerEnabled[0])
MQTTCheck();
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController >= 0) {
MQTTCheck(enabledMqttController);
}
#if defined(ESP8266)
if (Settings.UseSSDP)
SSDP_update();
+265 -81
View File
@@ -3,7 +3,7 @@
// this feature is not in all upstream versions yet.
// See https://github.com/esp8266/Arduino/issues/1923
// and https://github.com/letscontrolit/ESPEasy/issues/253
#include <md5.h>
#if defined(ESP8266)
void tcpCleanup()
{
@@ -712,70 +712,164 @@ unsigned long str2int(char *string)
return temp;
}
/********************************************************************************************\
Convert a char string to IP byte array
\*********************************************************************************************/
//FIXME: change original code so it uses IPAddress and IPAddress.fromString()
boolean str2ip(char *string, byte* IP)
boolean str2ip(const String& string, byte* IP) {
return str2ip(string.c_str(), IP);
}
boolean str2ip(const char *string, byte* IP)
{
byte c;
byte part = 0;
int value = 0;
for (unsigned int x = 0; x <= strlen(string); x++)
{
c = string[x];
if (isdigit(c))
{
value *= 10;
value += c - '0';
}
else if (c == '.' || c == 0) // next octet from IP address
{
if (value <= 255)
IP[part++] = value;
else
return false;
value = 0;
}
else if (c == ' ') // ignore these
;
else // invalid token
return false;
}
if (part == 4) // correct number of octets
IPAddress tmpip; // Default constructor => set to 0.0.0.0
if (*string == 0 || tmpip.fromString(string)) {
// Eiher empty string or a valid IP addres, so copy value.
for (byte i = 0; i < 4; ++i)
IP[i] = tmpip[i];
return true;
}
return false;
}
/********************************************************************************************\
check the program memory hash
The const MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere... needs to remain unchanged as it will be replaced by
- 16 bytes md5 hash, followed by
- 4 * uint32_t start of memory segment 1-4
- 4 * uint32_t end of memory segment 1-4
currently there are only two segemts included in the hash. Unused segments have start adress 0.
Execution time 520kb @80Mhz: 236ms
Returns: 0 if hash compare fails, number of checked bytes otherwise.
The reference hash is calculated by a .py file and injected into the binary.
Caution: currently the hash sits in an unchecked segment. If it ever moves to a checked segment, make sure
it is excluded from the calculation !
\*********************************************************************************************/
void dump (uint32_t addr){
Serial.print (addr,HEX);
Serial.print(": ");
for (uint32_t a = addr; a<addr+16; a++)
{
Serial.print ( pgm_read_byte(a),HEX);
Serial.print (" ");
}
Serial.println("");
}
uint32_t progMemMD5check(){
#define BufSize 10
const char CRCdummy[16+32+1] = "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere..."; // 16Bytes MD5, 32 Bytes Segment boundaries, 1Byte 0-termination. DO NOT MODIFY !
uint32_t calcBuffer[BufSize];
uint32_t md5NoOfBytes = 0;
memcpy (calcBuffer,CRCdummy,16); // is there still the dummy in memory ? - the dummy needs to be replaced by the real md5 after linking.
if( memcmp (calcBuffer, "MD5_MD5_MD5_MD5_",16)==0){ // do not memcmp with CRCdummy directly or it will get optimized away.
addLog(LOG_LEVEL_INFO, F("CRC : No program memory checksum found. Check output of crc2.py"));
return 0;
}
MD5Builder md5;
md5.begin();
for (int l = 0; l<4; l++){ // check max segments, if the pointer is not 0
uint32_t *ptrStart = (uint32_t *)&CRCdummy[16+l*4];
uint32_t *ptrEnd = (uint32_t *)&CRCdummy[16+4*4+l*4];
if ((*ptrStart) == 0) break; // segment not used.
for (uint32_t i = *ptrStart; i< (*ptrEnd) ; i=i+sizeof(calcBuffer)){ // "<" includes last byte
for (int buf = 0; buf < BufSize; buf ++){
calcBuffer[buf] = pgm_read_dword((void*)i+buf*4); // read 4 bytes
md5NoOfBytes+=sizeof(calcBuffer[0]);
}
md5.add((uint8_t *)&calcBuffer[0],(*ptrEnd-i)<sizeof(calcBuffer) ? (*ptrEnd-i):sizeof(calcBuffer) ); // add buffer to md5. At the end not the whole buffer. md5 ptr to data in ram.
}
}
md5.calculate();
md5.getBytes(thisBinaryMd5);
if ( memcmp (CRCdummy, thisBinaryMd5, 16) == 0 ) {
addLog(LOG_LEVEL_INFO, F("CRC : program checksum ...OK"));
return md5NoOfBytes;
}
addLog(LOG_LEVEL_INFO, F("CRC : program checksum ...FAIL"));
return 0;
}
/********************************************************************************************\
Save settings to SPIFFS
\*********************************************************************************************/
String SaveSettings(void)
{
MD5Builder md5;
memcpy( Settings.ProgmemMd5, thisBinaryMd5, 16);
md5.begin();
md5.add((uint8_t *)&Settings, sizeof(Settings)-16);
md5.calculate();
md5.getBytes(Settings.md5);
String err;
err=SaveToFile((char*)FILE_CONFIG, 0, (byte*)&Settings, sizeof(struct SettingsStruct));
if (err.length())
return(err);
return(err);
return(SaveToFile((char*)FILE_SECURITY, 0, (byte*)&SecuritySettings, sizeof(struct SecurityStruct)));
memcpy( SecuritySettings.ProgmemMd5, thisBinaryMd5, 16);
md5.begin();
md5.add((uint8_t *)&SecuritySettings, sizeof(SecuritySettings)-16);
md5.calculate();
md5.getBytes(SecuritySettings.md5);
err=SaveToFile((char*)FILE_SECURITY, 0, (byte*)&SecuritySettings, sizeof(struct SecurityStruct));
// dump((uint32_t)SecuritySettings.md5);
// dump((uint32_t)SecuritySettings.ProgmemMd5);
// dump((uint32_t)Settings.md5);
// dump((uint32_t)Settings.ProgmemMd5);
// dump((uint32_t)thisBinaryMd5);
return (err);
}
/********************************************************************************************\
Load settings from SPIFFS
\*********************************************************************************************/
String LoadSettings()
{
String err;
err=LoadFromFile((char*)FILE_CONFIG, 0, (byte*)&Settings, sizeof(struct SettingsStruct));
uint8_t calculatedMd5[16];
MD5Builder md5;
err=LoadFromFile((char*)FILE_CONFIG, 0, (byte*)&Settings, sizeof( SettingsStruct));
if (err.length())
return(err);
return(LoadFromFile((char*)FILE_SECURITY, 0, (byte*)&SecuritySettings, sizeof(struct SecurityStruct)));
md5.begin();
md5.add((uint8_t *)&Settings, sizeof(Settings)-16);
md5.calculate();
md5.getBytes(calculatedMd5);
if (memcmp (calculatedMd5, Settings.md5,16)==0){
addLog(LOG_LEVEL_INFO, F("CRC : Settings CRC ...OK"));
if (memcmp(Settings.ProgmemMd5, thisBinaryMd5, 16)!=0)
addLog(LOG_LEVEL_INFO, F("CRC : binary has changed since last save of Settings"));
}
else{
addLog(LOG_LEVEL_ERROR, F("CRC : Settings CRC ...FAIL"));
}
err=LoadFromFile((char*)FILE_SECURITY, 0, (byte*)&SecuritySettings, sizeof( SecurityStruct));
md5.begin();
md5.add((uint8_t *)&SecuritySettings, sizeof(SecuritySettings)-16);
md5.calculate();
md5.getBytes(calculatedMd5);
if (memcmp (calculatedMd5, SecuritySettings.md5, 16)==0){
addLog(LOG_LEVEL_INFO, F("CRC : SecuritySettings CRC ...OK "));
if (memcmp(SecuritySettings.ProgmemMd5,thisBinaryMd5, 16)!=0)
addLog(LOG_LEVEL_INFO, F("CRC : binary has changed since last save of Settings"));
}
else{
addLog(LOG_LEVEL_ERROR, F("CRC : SecuritySettings CRC ...FAIL"));
}
// dump((uint32_t)SecuritySettings.md5);
// dump((uint32_t)SecuritySettings.ProgmemMd5);
// dump((uint32_t)Settings.md5);
// dump((uint32_t)Settings.ProgmemMd5);
// dump((uint32_t)thisBinaryMd5);
return(err);
}
@@ -816,6 +910,15 @@ String SaveCustomTaskSettings(int TaskIndex, byte* memAddress, int datasize)
}
/********************************************************************************************\
Clear custom task settings
\*********************************************************************************************/
String ClearCustomTaskSettings(int TaskIndex)
{
// addLog(LOG_LEVEL_DEBUG, F("Clearing custom task settings"));
return(ClearInFile((char*)"config.dat", DAT_OFFSET_TASKS + (TaskIndex * DAT_TASKS_SIZE) + DAT_TASKS_CUSTOM_OFFSET, DAT_TASKS_SIZE));
}
/********************************************************************************************\
Load Custom Task settings to SPIFFS
\*********************************************************************************************/
@@ -848,6 +951,17 @@ String LoadControllerSettings(int ControllerIndex, byte* memAddress, int datasiz
return(LoadFromFile((char*)FILE_CONFIG, DAT_OFFSET_CONTROLLER + (ControllerIndex * DAT_CONTROLLER_SIZE), memAddress, datasize));
}
/********************************************************************************************\
Clear Custom Controller settings
\*********************************************************************************************/
String ClearCustomControllerSettings(int ControllerIndex)
{
// addLog(LOG_LEVEL_DEBUG, F("Clearing custom controller settings"));
return(ClearInFile((char*)"config.dat", DAT_OFFSET_CUSTOM_CONTROLLER + (ControllerIndex * DAT_CUSTOM_CONTROLLER_SIZE), DAT_CUSTOM_CONTROLLER_SIZE));
}
/********************************************************************************************\
Save Custom Controller settings to SPIFFS
\*********************************************************************************************/
@@ -941,6 +1055,27 @@ String SaveToFile(char* fname, int index, byte* memAddress, int datasize)
return String();
}
/********************************************************************************************\
Clear a certain area in a file (set to 0)
\*********************************************************************************************/
String ClearInFile(char* fname, int index, int datasize)
{
FLASH_GUARD();
fs::File f = SPIFFS.open(fname, "r+");
SPIFFS_CHECK(f, fname);
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
for (int x = 0; x < datasize ; x++)
{
SPIFFS_CHECK(f.write(0), fname);
}
f.close();
//OK
return String();
}
/********************************************************************************************\
Load data from config file on SPIFFS
@@ -1536,32 +1671,7 @@ String parseTemplate(String &tmpString, byte lineSize)
LoadTaskSettings(currentTaskIndex);
}
// replace other system variables like %sysname%, %systime%, %ip%
newString.replace(F("%sysname%"), Settings.Name);
newString.replace(F("%systime%"), getTimeString(':'));
newString.replace(F("%syshour%"), getHourString());
newString.replace(F("%sysmin%"), getMinuteString());
newString.replace(F("%syssec%"), getSecondString());
newString.replace(F("%sysday%"), getDayString());
newString.replace(F("%sysmonth%"), getMonthString());
newString.replace(F("%sysyear%"), getYearString());
newString.replace(F("%sysyears%"), getYearStringShort());
newString.replace(F("%uptime%"), String(wdcounter / 2));
#if FEATURE_ADC_VCC
newString.replace(F("%vcc%"), String(vcc));
#endif
const IPAddress ip = WiFi.localIP();
newString.replace(F("%ip%"), formatIP(ip));
newString.replace(F("%ip1%"), String(ip[0]));
newString.replace(F("%ip2%"), String(ip[1]));
newString.replace(F("%ip3%"), String(ip[2]));
newString.replace(F("%ip4%"), String(ip[3]));
newString.replace("%sysload%", String(100 - (100 * loopCounterLast / loopCounterMax)));
parseSystemVariables(newString, false);
// padding spaces
while (newString.length() < lineSize)
@@ -1571,6 +1681,75 @@ String parseTemplate(String &tmpString, byte lineSize)
}
/********************************************************************************************\
// replace other system variables like %sysname%, %systime%, %ip%
\*********************************************************************************************/
void repl(const String& key, const String& val, String& s, boolean useURLencode)
{
if (useURLencode) {
s.replace(key, URLEncode(val.c_str()));
} else {
s.replace(key, val);
}
}
void parseSystemVariables(String& s, boolean useURLencode)
{
#if FEATURE_ADC_VCC
repl(F("%vcc%"), String(vcc), s, useURLencode);
#endif
repl(F("%CR%"), F("\r"), s, useURLencode);
repl(F("%LF%"), F("\n"), s, useURLencode);
repl(F("%ip%"),WiFi.localIP().toString(), s, useURLencode);
repl(F("%sysload%"), String(100 - (100 * loopCounterLast / loopCounterMax)), s, useURLencode);
repl(F("%sysname%"), Settings.Name, s, useURLencode);
repl(F("%systime%"), getTimeString(':'), s, useURLencode);
char valueString[5];
sprintf_P(valueString, PSTR("%02d"), hour());
repl(F("%syshour%"), valueString, s, useURLencode);
sprintf_P(valueString, PSTR("%02d"), minute());
repl(F("%sysmin%"), valueString, s, useURLencode);
sprintf_P(valueString, PSTR("%02d"), second());
repl(F("%syssec%"), valueString, s, useURLencode);
sprintf_P(valueString, PSTR("%02d"), day());
repl(F("%sysday%"), valueString, s, useURLencode);
sprintf_P(valueString, PSTR("%02d"), month());
repl(F("%sysmonth%"),valueString, s, useURLencode);
sprintf_P(valueString, PSTR("%04d"), year());
repl(F("%sysyear%"), valueString, s, useURLencode);
sprintf_P(valueString, PSTR("%02d"), year()%100);
repl(F("%sysyears%"),valueString, s, useURLencode);
repl(F("%lcltime%"), getDateTimeString('-',':',' '), s, useURLencode);
repl(F("%tskname%"), ExtraTaskSettings.TaskDeviceName, s, useURLencode);
repl(F("%uptime%"), String(wdcounter / 2), s, useURLencode);
repl(F("%vname1%"), ExtraTaskSettings.TaskDeviceValueNames[0], s, useURLencode);
repl(F("%vname2%"), ExtraTaskSettings.TaskDeviceValueNames[1], s, useURLencode);
repl(F("%vname3%"), ExtraTaskSettings.TaskDeviceValueNames[2], s, useURLencode);
repl(F("%vname4%"), ExtraTaskSettings.TaskDeviceValueNames[3], s, useURLencode);
}
void parseEventVariables(String& s, struct EventStruct *event, boolean useURLencode)
{
repl(F("%id%"), String(event->idx), s, useURLencode);
if (event->sensorType == SENSOR_TYPE_LONG)
repl(F("%val1%"), String((unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16)), s, useURLencode);
else {
repl(F("%val1%"), toString(UserVar[event->BaseVarIndex + 0], ExtraTaskSettings.TaskDeviceValueDecimals[0]), s, useURLencode);
repl(F("%val2%"), toString(UserVar[event->BaseVarIndex + 1], ExtraTaskSettings.TaskDeviceValueDecimals[1]), s, useURLencode);
repl(F("%val3%"), toString(UserVar[event->BaseVarIndex + 2], ExtraTaskSettings.TaskDeviceValueDecimals[2]), s, useURLencode);
repl(F("%val4%"), toString(UserVar[event->BaseVarIndex + 3], ExtraTaskSettings.TaskDeviceValueDecimals[3]), s, useURLencode);
}
}
/********************************************************************************************\
Calculate function for simple expressions
\*********************************************************************************************/
@@ -2561,26 +2740,29 @@ void ArduinoOTAInit()
String getBearing(int degrees)
{
const char* bearing[] = {
PSTR("N"),
PSTR("NNE"),
PSTR("NE"),
PSTR("ENE"),
PSTR("E"),
PSTR("ESE"),
PSTR("SE"),
PSTR("SSE"),
PSTR("S"),
PSTR("SSW"),
PSTR("SW"),
PSTR("WSW"),
PSTR("W"),
PSTR("WNW"),
PSTR("NW"),
PSTR("NNW")
const __FlashStringHelper* bearing[] = {
F("N"),
F("NNE"),
F("NE"),
F("ENE"),
F("E"),
F("ESE"),
F("SE"),
F("SSE"),
F("S"),
F("SSW"),
F("SW"),
F("WSW"),
F("W"),
F("WNW"),
F("NW"),
F("NNW")
};
return(bearing[int(degrees/22.5)]);
int bearing_idx=int(degrees/22.5);
if (bearing_idx<0 || bearing_idx>=(int) (sizeof(bearing)/sizeof(bearing[0])))
return("");
else
return(bearing[bearing_idx]);
}
@@ -2610,3 +2792,5 @@ float compute_humidity_from_dewpoint(float temperature, float dew_temperature) {
return 100.0 * pow((112.0 - 0.1 * temperature + dew_temperature) /
(112.0 + 0.9 * temperature), 8);
}
+1 -5
View File
@@ -126,7 +126,6 @@ byte day()
return tm.Day;
}
byte hour()
{
return tm.Hour;
@@ -203,10 +202,8 @@ unsigned long getNtpTime()
else
WiFi.hostByName(ntpServerName, timeServerIP);
char host[20];
sprintf_P(host, PSTR("%u.%u.%u.%u"), timeServerIP[0], timeServerIP[1], timeServerIP[2], timeServerIP[3]);
log = F("NTP : NTP send to ");
log += host;
log += timeServerIP.toString();
addLog(LOG_LEVEL_DEBUG_MORE, log);
while (udp.parsePacket() > 0) ; // discard any previously received packets
@@ -414,7 +411,6 @@ String getDateTimeString(char dateDelimiter, char timeDelimiter, char dateTimeD
return getDateTimeString(tm, dateDelimiter, timeDelimiter, dateTimeDelimiter);
}
/********************************************************************************************\
Convert a string like "Sun,12:30" into a 32 bit integer
\*********************************************************************************************/
+84 -36
View File
@@ -261,6 +261,7 @@ void getWebPageTemplateDefault(const String& tmplName, String& tmpl)
"{{css}}"
"</head>"
"<body class='bodymenu'>"
"<b style=\"color:red\" id='rbtmsg'></b>"
"<header class='headermenu'>"
"<h1>ESP Easy Mega: {{name}} {{logo}}</h1>"
"{{menu}}"
@@ -371,6 +372,51 @@ void processAndSendWebPageTemplate(String& pageTemplate, String& pageContent)
pageTemplate = F(""); //free mem
sendWebPageChunkedData(log, pageResult); //send the rest of the accumulated HTML
if (shouldReboot)
{
//we only add this here as a seperate chucnk to prevent using too much memory at once
pageResult+=F(
"<script>"
"i=document.getElementById('rbtmsg');"
"i.innerHTML=\"Please reboot: <input id='reboot' class='button link' value='Reboot' type='submit' onclick='r()'>\";"
"var x = new XMLHttpRequest();"
//done
"function d(){"
"i.innerHTML='';"
"clearTimeout(t);"
"}"
//keep requesting mainpage until no more errors
"function c(){"
"i.innerHTML+='.';"
"x.onload=d;"
"x.open('GET', window.location.origin);"
"x.send();"
"}"
//rebooting
"function b(){"
"i.innerHTML='Rebooting..';"
"t=setInterval(c,2000);"
"}"
//request reboot
"function r(){"
"i.innerHTML+=' (requesting)';"
"x.onload=b;"
"x.open('GET', window.location.origin+'/?cmd=reboot');"
"x.send();"
"}"
"</script>"
);
sendWebPageChunkedData(log, pageResult); //send the rest of the accumulated HTML
}
sendWebPageChunkedEnd(log); //close chunked send
addLog(LOG_LEVEL_DEBUG_DEV, log);
@@ -703,8 +749,6 @@ void handle_config() {
if (timerAPoff)
timerAPoff = millis() + 2000L; //user has reached the main page - AP can be switched off in 2..3 sec
char tmpString[64];
navMenuIndex = 1;
String name = WebServer.arg(F("name"));
//String password = WebServer.arg(F("password"));
@@ -757,24 +801,20 @@ void handle_config() {
}
case ONLY_IP_RANGE_ALLOWED:
case ALL_ALLOWED:
iprangelow.toCharArray(tmpString, 26);
str2ip(tmpString, SecuritySettings.AllowedIPrangeLow);
iprangehigh.toCharArray(tmpString, 26);
str2ip(tmpString, SecuritySettings.AllowedIPrangeHigh);
// iprangelow.toCharArray(tmpString, 26);
str2ip(iprangelow, SecuritySettings.AllowedIPrangeLow);
// iprangehigh.toCharArray(tmpString, 26);
str2ip(iprangehigh, SecuritySettings.AllowedIPrangeHigh);
break;
}
Settings.Delay = sensordelay.toInt();
Settings.deepSleep = (deepsleep == "on");
Settings.deepSleepOnFail = (deepsleeponfail == "on");
espip.toCharArray(tmpString, 26);
str2ip(tmpString, Settings.IP);
espgateway.toCharArray(tmpString, 26);
str2ip(tmpString, Settings.Gateway);
espsubnet.toCharArray(tmpString, 26);
str2ip(tmpString, Settings.Subnet);
espdns.toCharArray(tmpString, 26);
str2ip(tmpString, Settings.DNS);
str2ip(espip, Settings.IP);
str2ip(espgateway, Settings.Gateway);
str2ip(espsubnet, Settings.Subnet);
str2ip(espdns, Settings.DNS);
Settings.Unit = unit.toInt();
addHtmlError(reply, SaveSettings());
}
@@ -850,8 +890,6 @@ void handle_controllers() {
if (!isLoggedIn()) return;
struct EventStruct TempEvent;
char tmpString[64];
navMenuIndex = 2;
byte controllerindex = WebServer.arg(F("index")).toInt();
boolean controllerNotSet = controllerindex == 0;
@@ -879,21 +917,28 @@ void handle_controllers() {
if (Settings.Protocol[controllerindex] != protocol.toInt())
{
//reset (some) default-settings
Settings.Protocol[controllerindex] = protocol.toInt();
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[controllerindex]);
ControllerSettings.Port = Protocol[ProtocolIndex].defaultPort;
if (Protocol[ProtocolIndex].usesTemplate)
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_TEMPLATE, &TempEvent, dummyString);
strncpy(ControllerSettings.Subscribe, TempEvent.String1.c_str(), sizeof(ControllerSettings.Subscribe));
strncpy(ControllerSettings.Publish, TempEvent.String2.c_str(), sizeof(ControllerSettings.Publish));
TempEvent.String1 = "";
TempEvent.String2 = "";
//NOTE: do not enable controller by default, give user a change to enter sensible values first
//not resetted to default (for convenience)
//SecuritySettings.ControllerUser[controllerindex]
//SecuritySettings.ControllerPassword[controllerindex]
//there is a protocol selected?
if (Settings.Protocol[controllerindex]!=0)
{
//reset (some) default-settings
byte ProtocolIndex = getProtocolIndex(Settings.Protocol[controllerindex]);
ControllerSettings.Port = Protocol[ProtocolIndex].defaultPort;
if (Protocol[ProtocolIndex].usesTemplate)
CPlugin_ptr[ProtocolIndex](CPLUGIN_PROTOCOL_TEMPLATE, &TempEvent, dummyString);
strncpy(ControllerSettings.Subscribe, TempEvent.String1.c_str(), sizeof(ControllerSettings.Subscribe));
strncpy(ControllerSettings.Publish, TempEvent.String2.c_str(), sizeof(ControllerSettings.Publish));
TempEvent.String1 = "";
TempEvent.String2 = "";
//NOTE: do not enable controller by default, give user a change to enter sensible values first
//not resetted to default (for convenience)
//SecuritySettings.ControllerUser[controllerindex]
//SecuritySettings.ControllerPassword[controllerindex]
ClearCustomControllerSettings(controllerindex);
}
}
@@ -920,11 +965,7 @@ void handle_controllers() {
//no protocol selected
else
{
if (controllerip.length() != 0)
{
controllerip.toCharArray(tmpString, 26);
str2ip(tmpString, ControllerSettings.IP);
}
str2ip(controllerip, ControllerSettings.IP);
}
//copy settings to struct
Settings.ControllerEnabled[controllerindex] = (controllerenabled == "on");
@@ -1482,12 +1523,15 @@ void handle_devices() {
PluginCall(PLUGIN_EXIT, &TempEvent, dummyString);
taskClear(taskIndex, false); // clear settings, but do not save
Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber;
if (taskdevicenumber != 0) // set default values if a new device has been selected
{
//NOTE: do not enable task by default. allow user to enter sensible valus first and let him enable it when ready.
if (ExtraTaskSettings.TaskDeviceValueNames[0][0] == 0) // if field set empty, reload defaults
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, &TempEvent, dummyString); //the plugin should populate ExtraTaskSettings with its default values.
ClearCustomTaskSettings(taskIndex);
}
}
else if (taskdevicenumber != 0) //save settings
@@ -3126,6 +3170,8 @@ void handle_json()
reply += BUILD;
reply += F(",\n\"Unit\": ");
reply += Settings.Unit;
reply += F(",\n\"Local time\": ");
reply += getDateTimeString('-',':',' ');
reply += F(",\n\"Uptime\": ");
reply += wdcounter / 2;
reply += F(",\n\"Free RAM\": ");
@@ -3230,8 +3276,7 @@ void handle_advanced() {
ntphost.toCharArray(tmpString, 64);
strcpy(Settings.NTPHost, tmpString);
Settings.TimeZone = timezone.toInt();
syslogip.toCharArray(tmpString, 26);
str2ip(tmpString, Settings.Syslog_IP);
str2ip(syslogip.c_str(), Settings.Syslog_IP);
Settings.UDPPort = udpport.toInt();
Settings.SyslogLevel = sysloglevel.toInt();
Settings.UseSerial = (useserial == "on");
@@ -4381,6 +4426,9 @@ void handle_sysinfo() {
#ifdef PLUGIN_BUILD_DEV
reply += F(" [Development]");
#endif
reply += F("<TR><TD>Md5<TD>");
for (byte i = 0; i<16; i++) reply += String(thisBinaryMd5[i],HEX);
reply += F("<TR><TD colspan=2><H3>ESP board</H3></TD></TR>");
+4 -4
View File
@@ -101,9 +101,9 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
case SENSOR_TYPE_TEMP_BARO: // temp + bar used for BMP085 and BMP280
url += F("&svalue=");
url += formatUserVar(event, 0);
url += F(";");
url += formatUserVar(event, 1);
url += F(";0;0;");
url += formatUserVar(event, 1);
url += F(";0");
break;
case SENSOR_TYPE_TRIPLE:
url += F("&svalue=");
@@ -165,7 +165,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
url += toString((UserVar[event->BaseVarIndex + 1] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[1]);
url += ";";
url += toString((UserVar[event->BaseVarIndex + 2] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[2]);
url += ";0";
url += ";0;0";
break;
}
@@ -182,7 +182,7 @@ boolean CPlugin_001(byte function, struct EventStruct *event, String& string)
client.print(request);
unsigned long timer = millis() + 200;
while (!client.available() && !timeOutReached(timer))
while (!client.available() && !timeOutReached(timer))
yield();
// Read all the lines of the reply from server and log them
+99 -81
View File
@@ -44,81 +44,88 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
// char json[512];
// json[0] = 0;
// event->String2.toCharArray(json, 512);
// Find first enabled controller index with this protocol
byte ControllerID = CONTROLLER_MAX;
for (byte i=0; i < CONTROLLER_MAX; i++) {
if (Settings.Protocol[i] == CPLUGIN_ID_002 && Settings.ControllerEnabled[i]) {
ControllerID = i;
}
}
if (ControllerID < CONTROLLER_MAX) {
StaticJsonBuffer<512> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(event->String2.c_str());
if (root.success())
{
unsigned int idx = root[F("idx")];
float nvalue = root[F("nvalue")];
long nvaluealt = root[F("nvalue")];
//const char* name = root["name"]; // Not used
//const char* svalue = root["svalue"]; // Not used
const char* svalue1 = root[F("svalue1")];
//const char* svalue2 = root["svalue2"]; // Not used
//const char* svalue3 = root["svalue3"]; // Not used
const char* switchtype = root[F("switchType")]; // Expect "On/Off" or "dimmer"
if (nvalue == 0)
nvalue = nvaluealt;
if ((int)switchtype == 0)
switchtype = "?";
StaticJsonBuffer<512> jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(event->String2.c_str());
if (root.success())
{
unsigned int idx = root[F("idx")];
float nvalue = root[F("nvalue")];
long nvaluealt = root[F("nvalue")];
//const char* name = root["name"]; // Not used
//const char* svalue = root["svalue"]; // Not used
const char* svalue1 = root[F("svalue1")];
//const char* svalue2 = root["svalue2"]; // Not used
//const char* svalue3 = root["svalue3"]; // Not used
const char* switchtype = root[F("switchType")]; // Expect "On/Off" or "dimmer"
if (nvalue == 0)
nvalue = nvaluealt;
if ((int)switchtype == 0)
switchtype = "?";
for (byte x = 0; x < TASKS_MAX; x++) {
// We need the index of the controller we are: 0-CONTROLLER_MAX
byte ControllerID = 0;
for (byte i=0; i < CONTROLLER_MAX; i++)
{
if (Settings.Protocol[i] == CPLUGIN_ID_002) { ControllerID = i; }
}
if (Settings.TaskDeviceID[ControllerID][x] == idx) // get idx for our controller index
{
if (Settings.TaskDeviceNumber[x] == 1) // temp solution, if input switch, update state
{
String action = F("inputSwitchState,");
action += x;
action += ",";
action += nvalue;
struct EventStruct TempEvent;
parseCommandString(&TempEvent, action);
PluginCall(PLUGIN_WRITE, &TempEvent, action);
}
if (Settings.TaskDeviceNumber[x] == 29) // temp solution, if plugin 029, set gpio
for (byte x = 0; x < TASKS_MAX; x++) {
// We need the index of the controller we are: 0-CONTROLLER_MAX
if (Settings.TaskDeviceEnabled[x] && Settings.TaskDeviceID[ControllerID][x] == idx) // get idx for our controller index
{
String action = "";
int baseVar = x * VARS_PER_TASK;
struct EventStruct TempEvent;
if (strcasecmp_P(switchtype, PSTR("dimmer")) == 0)
{
int pwmValue = UserVar[baseVar];
action = F("pwm,");
action += Settings.TaskDevicePin1[x];
action += ",";
switch ((int)nvalue)
switch (Settings.TaskDeviceNumber[x]) {
case 1: // temp solution, if input switch, update state
{
case 0:
pwmValue = 0;
break;
case 1:
pwmValue = UserVar[baseVar];
break;
case 2:
pwmValue = 10 * atol(svalue1);
UserVar[baseVar] = pwmValue;
break;
String action = F("inputSwitchState,");
action += x;
action += ",";
action += nvalue;
break;
}
action += pwmValue;
case 29: // temp solution, if plugin 029, set gpio
{
String action = "";
int baseVar = x * VARS_PER_TASK;
struct EventStruct TempEvent;
if (strcasecmp_P(switchtype, PSTR("dimmer")) == 0)
{
int pwmValue = UserVar[baseVar];
action = F("pwm,");
action += Settings.TaskDevicePin1[x];
action += ",";
switch ((int)nvalue)
{
case 0:
pwmValue = 0;
break;
case 1:
pwmValue = UserVar[baseVar];
break;
case 2:
pwmValue = 10 * atol(svalue1);
UserVar[baseVar] = pwmValue;
break;
}
action += pwmValue;
} else {
UserVar[baseVar] = nvalue;
action = F("gpio,");
action += Settings.TaskDevicePin1[x];
action += ",";
action += nvalue;
}
break;
}
default:
break;
}
else
{
UserVar[baseVar] = nvalue;
action = F("gpio,");
action += Settings.TaskDevicePin1[x];
action += ",";
action += nvalue;
if (action.length() > 0) {
struct EventStruct TempEvent;
parseCommandString(&TempEvent, action);
PluginCall(PLUGIN_WRITE, &TempEvent, action);
}
parseCommandString(&TempEvent, action);
PluginCall(PLUGIN_WRITE, &TempEvent, action);
}
}
}
@@ -172,11 +179,11 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
break;
case SENSOR_TYPE_TRIPLE: // any sensor that uses three simple values
root[F("nvalue")] = 0;
values = toString(UserVar[event->BaseVarIndex ], ExtraTaskSettings.TaskDeviceValueDecimals[0]);
values = formatUserVar(event, 0);
values += ";";
values += toString(UserVar[event->BaseVarIndex + 1], ExtraTaskSettings.TaskDeviceValueDecimals[1]);
values += formatUserVar(event, 1);
values += ";";
values += toString(UserVar[event->BaseVarIndex + 2], ExtraTaskSettings.TaskDeviceValueDecimals[2]);
values += formatUserVar(event, 2);
// values.toCharArray(str, 80);
root[F("svalue")] = values.c_str();
// root[F("svalue")] = str;
@@ -194,8 +201,9 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
case SENSOR_TYPE_TEMP_BARO: // temp + hum + hum_stat + bar + bar_fore, used for BMP085
root[F("nvalue")] = 0;
values = formatUserVar(event, 0);
values += formatUserVar(event, 1);
values += ";0;0;";
values += formatUserVar(event, 1);
values += ";0";
// values.toCharArray(str, 80);
root[F("svalue")] = values.c_str();
// root[F("svalue")] = str;
@@ -212,6 +220,19 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
// values.toCharArray(str, 80);
// root[F("svalue")] = str;
break;
case SENSOR_TYPE_QUAD:
root[F("nvalue")] = 0;
values = formatUserVar(event, 0);
values += ";";
values += formatUserVar(event, 1);
values += ";";
values += formatUserVar(event, 2);
values += ";";
values += formatUserVar(event, 3);
root[F("svalue")] = values.c_str();
// values.toCharArray(str, 80);
// root[F("svalue")] = str;
break;
case SENSOR_TYPE_SWITCH:
root[F("command")] = String(F("switchlight"));
if (UserVar[event->BaseVarIndex] == 0)
@@ -229,7 +250,7 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
case SENSOR_TYPE_WIND: // WindDir in degrees; WindDir as text; Wind speed average ; Wind speed gust
values = formatUserVar(event, 0);
values += ";";
values += getBearing(int(UserVar[event->BaseVarIndex] / 22.5));
values += getBearing(UserVar[event->BaseVarIndex]);
values += ";";
// Domoticz expects the wind speed in (m/s * 10)
values += toString((UserVar[event->BaseVarIndex + 1] * 10),ExtraTaskSettings.TaskDeviceValueDecimals[1]);
@@ -240,6 +261,8 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
// values.toCharArray(str, 80);
// root["svalue"] = str;
break;
}
String json;
@@ -249,15 +272,10 @@ boolean CPlugin_002(byte function, struct EventStruct *event, String& string)
addLog(LOG_LEVEL_DEBUG, log);
String pubname = ControllerSettings.Publish;
pubname.replace(F("%sysname%"), Settings.Name);
pubname.replace(F("%tskname%"), ExtraTaskSettings.TaskDeviceName);
pubname.replace(F("%id%"), String(event->idx));
if (!MQTTclient.publish(pubname.c_str(), json.c_str(), Settings.MQTTRetainFlag))
parseSystemVariables(pubname, false);
parseEventVariables(pubname, event, false);
if (!MQTTpublish(event->ControllerIndex, pubname.c_str(), json.c_str(), Settings.MQTTRetainFlag))
{
log = F("MQTT : publish failed");
addLog(LOG_LEVEL_DEBUG, log);
MQTTConnect();
connectionFailures++;
}
else if (connectionFailures)
+5 -5
View File
@@ -57,8 +57,8 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
url += formatUserVar(event, 0);
url += "\n";
strcpy_P(log, PSTR("TELNT: Sending enter"));
addLog(LOG_LEVEL_ERROR, log);
// strcpy_P(log, PSTR("TELNT: Sending enter"));
// addLog(LOG_LEVEL_ERROR, log);
client.print(" \n");
unsigned long timer = millis() + 200;
@@ -77,20 +77,20 @@ boolean CPlugin_003(byte function, struct EventStruct *event, String& string)
{
success = true;
strcpy_P(log, PSTR("TELNT: Password request ok"));
addLog(LOG_LEVEL_ERROR, log);
addLog(LOG_LEVEL_DEBUG, log);
}
delay(1);
}
strcpy_P(log, PSTR("TELNT: Sending pw"));
addLog(LOG_LEVEL_ERROR, log);
addLog(LOG_LEVEL_DEBUG, log);
client.println(SecuritySettings.ControllerPassword[event->ControllerIndex]);
delay(100);
while (client.available())
client.read();
strcpy_P(log, PSTR("TELNT: Sending cmd"));
addLog(LOG_LEVEL_ERROR, log);
addLog(LOG_LEVEL_DEBUG, log);
client.print(url);
delay(10);
while (client.available())
+4 -4
View File
@@ -96,9 +96,8 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = ControllerSettings.Publish;
pubname.replace(F("%sysname%"), Settings.Name);
pubname.replace(F("%tskname%"), ExtraTaskSettings.TaskDeviceName);
pubname.replace(F("%id%"), String(event->idx));
parseSystemVariables(pubname, false);
parseEventVariables(pubname, event, false);
String value = "";
// byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[event->TaskIndex]);
@@ -111,7 +110,8 @@ boolean CPlugin_005(byte function, struct EventStruct *event, String& string)
value = (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
else
value = formatUserVar(event, x);
MQTTclient.publish(tmppubname.c_str(), value.c_str(), Settings.MQTTRetainFlag);
MQTTpublish(event->ControllerIndex, tmppubname.c_str(), value.c_str(), Settings.MQTTRetainFlag);
String log = F("MQTT : ");
log += tmppubname;
log += " ";
+3 -4
View File
@@ -89,9 +89,8 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummyString);
String pubname = ControllerSettings.Publish;
pubname.replace(F("%sysname%"), Settings.Name);
pubname.replace(F("%tskname%"), ExtraTaskSettings.TaskDeviceName);
pubname.replace(F("%id%"), String(event->idx));
parseSystemVariables(pubname, false);
parseEventVariables(pubname, event, false);
String value = "";
// byte DeviceIndex = getDeviceIndex(Settings.TaskDeviceNumber[event->TaskIndex]);
@@ -104,7 +103,7 @@ boolean CPlugin_006(byte function, struct EventStruct *event, String& string)
value = (unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16);
else
value = formatUserVar(event, x);
MQTTclient.publish(tmppubname.c_str(), value.c_str(), Settings.MQTTRetainFlag);
MQTTpublish(event->ControllerIndex, tmppubname.c_str(), value.c_str(), Settings.MQTTRetainFlag);
}
break;
}
+3 -4
View File
@@ -105,10 +105,9 @@ boolean HTTPSend(struct EventStruct *event, byte varIndex, float value, unsigned
String url = "/";
url += ControllerSettings.Publish;
//TODO: move this to a generic replacement function?
url.replace(F("%sysname%"), URLEncode(Settings.Name));
url.replace(F("%tskname%"), URLEncode(ExtraTaskSettings.TaskDeviceName));
url.replace(F("%id%"), String(event->idx));
parseSystemVariables(url, true);
parseEventVariables(url, event, true);
url.replace(F("%valname%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[varIndex]));
if (longValue)
url.replace(F("%value%"), String(longValue));
+2
View File
@@ -80,6 +80,8 @@ void C010_Send(struct EventStruct *event, byte varIndex, float value, unsigned l
String msg = "";
msg += ControllerSettings.Publish;
parseSystemVariables(msg, false);
parseEventVariables(msg, event, false);
msg.replace(F("%sysname%"), Settings.Name);
msg.replace(F("%tskname%"), ExtraTaskSettings.TaskDeviceName);
msg.replace(F("%id%"), String(event->idx));
+2 -32
View File
@@ -306,39 +306,9 @@ void ReplaceTokenByValue(String& s, struct EventStruct *event)
addLog(LOG_LEVEL_DEBUG_MORE, s);
//NOTE: cant we just call parseTemplate() for all the standard stuff??
parseSystemVariables(s, true);
parseEventVariables(s, event, true);
s.replace(F("%systime%"), getTimeString(':'));
#if FEATURE_ADC_VCC
s.replace(F("%vcc%"), String(vcc));
#endif
// IPAddress ip = WiFi.localIP();
// char strIP[20];
// sprintf_P(strIP, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
s.replace(F("%ip%"),WiFi.localIP().toString());
s.replace(F("%sysload%"), String(100 - (100 * loopCounterLast / loopCounterMax)));
s.replace(F("%uptime%"), String(wdcounter / 2));
s.replace(F("%CR%"), F("\r"));
s.replace(F("%LF%"), F("\n"));
s.replace(F("%sysname%"), URLEncode(Settings.Name));
s.replace(F("%tskname%"), URLEncode(ExtraTaskSettings.TaskDeviceName));
s.replace(F("%id%"), String(event->idx));
s.replace(F("%vname1%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[0]));
s.replace(F("%vname2%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[1]));
s.replace(F("%vname3%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[2]));
s.replace(F("%vname4%"), URLEncode(ExtraTaskSettings.TaskDeviceValueNames[3]));
if (event->sensorType == SENSOR_TYPE_LONG)
s.replace(F("%val1%"), String((unsigned long)UserVar[event->BaseVarIndex] + ((unsigned long)UserVar[event->BaseVarIndex + 1] << 16)));
else {
s.replace(F("%val1%"), formatUserVar(event, 0));
s.replace(F("%val2%"), formatUserVar(event, 1));
s.replace(F("%val3%"), formatUserVar(event, 2));
s.replace(F("%val4%"), formatUserVar(event, 3));
}
addLog(LOG_LEVEL_DEBUG_MORE, F("HTTP after replacements: "));
addLog(LOG_LEVEL_DEBUG_MORE, s);
}
+21 -5
View File
@@ -117,20 +117,27 @@ boolean Plugin_004(byte function, struct EventStruct * event, String& string)
{
if (x != 0)
string += "-";
string += String(ExtraTaskSettings.TaskDevicePluginConfigLong[x], HEX);
// string += String(ExtraTaskSettings.TaskDevicePluginConfigLong[x], HEX);
}
success = true;
break;
}
case PLUGIN_INIT:
{
Plugin_004_DallasPin = Settings.TaskDevicePin1[event->TaskIndex];
uint8_t addr[8];
Plugin_004_get_addr(addr, event->TaskIndex);
Plugin_004_DS_startConvertion(addr);
delay(800); //give it time to do intial conversion
success = true;
break;
}
case PLUGIN_READ:
{
if (ExtraTaskSettings.TaskDevicePluginConfigLong[0] != 0){
uint8_t addr[8];
// Load ROM address from tasksettings
LoadTaskSettings(event->TaskIndex);
for (byte x = 0; x < 8; x++)
addr[x] = ExtraTaskSettings.TaskDevicePluginConfigLong[x];
Plugin_004_get_addr(addr, event->TaskIndex);
Plugin_004_DallasPin = Settings.TaskDevicePin1[event->TaskIndex];
float value = 0;
@@ -166,6 +173,15 @@ boolean Plugin_004(byte function, struct EventStruct * event, String& string)
return success;
}
void Plugin_004_get_addr(uint8_t addr[], byte TaskIndex)
{
// Load ROM address from tasksettings
LoadTaskSettings(TaskIndex);
for (byte x = 0; x < 8; x++)
addr[x] = ExtraTaskSettings.TaskDevicePluginConfigLong[x];
}
/*********************************************************************************************\
Dallas Scan bus
\*********************************************************************************************/
+99 -83
View File
@@ -4,7 +4,7 @@
#define PLUGIN_005
#define PLUGIN_ID_005 5
#define PLUGIN_NAME_005 "Environment - DHT11/12/22"
#define PLUGIN_NAME_005 "Environment - DHT11/12/22 SONOFF2301/7021"
#define PLUGIN_VALUENAME1_005 "Temperature"
#define PLUGIN_VALUENAME2_005 "Humidity"
@@ -47,10 +47,10 @@ boolean Plugin_005(byte function, struct EventStruct *event, String& string)
case PLUGIN_WEBFORM_LOAD:
{
const String options[] = { F("DHT 11"), F("DHT 22"), F("DHT 12") };
int indices[] = { 11, 22, 12 };
const String options[] = { F("DHT 11"), F("DHT 22"), F("DHT 12"), F("Sonoff am2301"), F("Sonoff si7021") };
int indices[] = { 11, 22, 12, 23, 70 };
addFormSelector(string, F("DHT Type"), F("plugin_005_dhttype"), 3, options, indices, Settings.TaskDevicePluginConfig[event->TaskIndex][0] );
addFormSelector(string, F("DHT Type"), F("plugin_005_dhttype"), 5, options, indices, Settings.TaskDevicePluginConfig[event->TaskIndex][0] );
success = true;
break;
@@ -67,96 +67,91 @@ boolean Plugin_005(byte function, struct EventStruct *event, String& string)
case PLUGIN_READ:
{
byte dht_dat[5];
byte dht_in;
byte i;
// byte Retry = 0;
boolean error = false;
byte Par3 = Settings.TaskDevicePluginConfig[event->TaskIndex][0];
Plugin_005_DHT_Pin = Settings.TaskDevicePin1[event->TaskIndex];
pinMode(Plugin_005_DHT_Pin, OUTPUT);
// DHT start condition, pull-down i/o pin for 18ms
digitalWrite(Plugin_005_DHT_Pin, LOW); // Pull low
delay(18);
digitalWrite(Plugin_005_DHT_Pin, HIGH); // Pull high
delayMicroseconds(20); // was 40
if(Par3 == 11 || Par3 == 22 || Par3 == 12) delay(18);
else if (Par3 == 23 ) delayMicroseconds(900);
else if (Par3 == 70 ) delayMicroseconds(500);
pinMode(Plugin_005_DHT_Pin, INPUT); // change pin to input
delayMicroseconds(10);
delayMicroseconds(50);
dht_in = digitalRead(Plugin_005_DHT_Pin);
if (!dht_in)
error = waitState(0);
if(error)
{ logError(event, F("DHT : no Reading !"));
break;
}
error = waitState(1);
if(error)
{ logError(event, F("DHT : no Reading !"));
break;
}
noInterrupts();
error = waitState(0);
if(error)
{ logError(event, F("DHT : no Reading !"));
break;
}
for (i = 0; i < 5; i++)
{
delayMicroseconds(80);
dht_in = digitalRead(Plugin_005_DHT_Pin);
if (dht_in)
{
delayMicroseconds(80); // now ready for data reception
for (i = 0; i < 5; i++)
{
byte data = Plugin_005_read_dht_dat();
if (data != -1)
dht_dat[i] = data;
else
{
addLog(LOG_LEVEL_ERROR, F("DHT : protocol timeout!"));
error = true;
}
byte data = Plugin_005_read_dht_dat();
if(data == -1)
{ logError(event, "DHT : protocol timeout!");
break;
}
if (!error)
{
dht_dat[i] = data;
}
interrupts();
// Checksum calculation is a Rollover Checksum by design!
byte dht_check_sum = dht_dat[0] + dht_dat[1] + dht_dat[2] + dht_dat[3]; // check check_sum
if (dht_dat[4] == dht_check_sum)
{
float temperature = NAN;
float humidity = NAN;
if (Par3 == 11)
{
temperature = float(dht_dat[2]); // Temperature
humidity = float(dht_dat[0]); // Humidity
}
else if (Par3 == 12)
{
temperature = float(dht_dat[2]*10 + (dht_dat[3] & 0x7f)) / 10.0; // Temperature
if (dht_dat[3] & 0x80) { temperature = -temperature; } // Negative temperature
humidity = float(dht_dat[0]*10+dht_dat[1]) / 10.0; // Humidity
}
if (Par3 == 22)
{
if (dht_dat[2] & 0x80) // negative temperature
temperature = -0.1 * word(dht_dat[2] & 0x7F, dht_dat[3]);
else
temperature = 0.1 * word(dht_dat[2], dht_dat[3]);
humidity = word(dht_dat[0], dht_dat[1]) * 0.1; // Humidity
}
if (temperature != NAN || humidity != NAN) // According to negated original if, maybe use && instead?
{
UserVar[event->BaseVarIndex] = temperature;
UserVar[event->BaseVarIndex + 1] = humidity;
String log = F("DHT : Temperature: ");
log += UserVar[event->BaseVarIndex];
addLog(LOG_LEVEL_INFO, log);
log = F("DHT : Humidity: ");
log += UserVar[event->BaseVarIndex + 1];
addLog(LOG_LEVEL_INFO, log);
success = true;
}
} // checksum
} // error
} // dht
} // !dht
if(!success)
byte dht_check_sum = (dht_dat[0] + dht_dat[1] + dht_dat[2] + dht_dat[3]) & 0xFF; // check check_sum
if (dht_dat[4] != dht_check_sum)
{
addLog(LOG_LEVEL_INFO, F("DHT : No reading!"));
UserVar[event->BaseVarIndex] = NAN;
UserVar[event->BaseVarIndex + 1] = NAN;
logError(event, "DHT : checksum error!");
break;
}
float temperature = NAN;
float humidity = NAN;
if (Par3 == 11)
{
temperature = float(dht_dat[2]); // Temperature
humidity = float(dht_dat[0]); // Humidity
}
else if (Par3 == 12)
{
temperature = float(dht_dat[2]*10 + (dht_dat[3] & 0x7f)) / 10.0; // Temperature
if (dht_dat[3] & 0x80) { temperature = -temperature; } // Negative temperature
humidity = float(dht_dat[0]*10+dht_dat[1]) / 10.0; // Humidity
}
else if (Par3 == 22 || Par3 == 23 || Par3 == 70)
{
if (dht_dat[2] & 0x80) // negative temperature
temperature = -0.1 * word(dht_dat[2] & 0x7F, dht_dat[3]);
else
temperature = 0.1 * word(dht_dat[2], dht_dat[3]);
humidity = 0.1 * word(dht_dat[0], dht_dat[1]); // Humidity
}
if (temperature == NAN || humidity == NAN)
{ logError(event, "DHT : invalid NAN reading !");
break;
}
UserVar[event->BaseVarIndex] = temperature;
UserVar[event->BaseVarIndex + 1] = humidity;
String log = F("DHT : Temperature: ");
log += UserVar[event->BaseVarIndex];
addLog(LOG_LEVEL_INFO, log);
log = F("DHT : Humidity: ");
log += UserVar[event->BaseVarIndex + 1];
addLog(LOG_LEVEL_INFO, log);
success = true;
break;
}
}
@@ -164,6 +159,31 @@ boolean Plugin_005(byte function, struct EventStruct *event, String& string)
}
/*********************************************************************************************\
* DHT sub to log an error
\*********************************************************************************************/
void logError(struct EventStruct *event, String text)
{
addLog(LOG_LEVEL_INFO, text);
UserVar[event->BaseVarIndex] = NAN;
UserVar[event->BaseVarIndex + 1] = NAN;
}
/*********************************************************************************************\
* DHT sub to wait until a pin is in a certiin state
\*********************************************************************************************/
boolean waitState(int state)
{
byte counter = 0;
while (( digitalRead(Plugin_005_DHT_Pin) != state) && (counter < 100))
{
delayMicroseconds(1);
counter++;
}
if( counter < 100) return false;
return true;
}
/*********************************************************************************************\
* DHT sub to get an 8 bit value from the receiving bitstream
\*********************************************************************************************/
@@ -172,7 +192,6 @@ int Plugin_005_read_dht_dat(void)
byte i = 0;
byte result = 0;
byte counter = 0;
//noInterrupts();
for (i = 0; i < 8; i++)
{
while ((!digitalRead(Plugin_005_DHT_Pin)) && (counter < 100))
@@ -182,10 +201,9 @@ int Plugin_005_read_dht_dat(void)
}
if (counter >= 100)
{
//interrupts();
return -1;
}
delayMicroseconds(30);
delayMicroseconds(35); // was 30
if (digitalRead(Plugin_005_DHT_Pin))
result |= (1 << (7 - i));
counter = 0;
@@ -196,10 +214,8 @@ int Plugin_005_read_dht_dat(void)
}
if (counter >= 100)
{
//interrupts();
return -1;
}
}
//interrupts();
return result;
}
+15 -14
View File
@@ -324,21 +324,23 @@ void mqttcallback_037(char* c_topic, byte* b_payload, unsigned int length)
// It would be nice to understand this....
boolean MQTTConnect_037(String clientid)
{
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(0, (byte*)&ControllerSettings, sizeof(ControllerSettings)); // todo index is now fixed to 0
boolean result = false;
// @ToDo TD-er: Plugin allows for more than one MQTT controller, but we're now using only the first enabled one.
int enabledMqttController = firstEnabledMQTTController();
if (enabledMqttController < 0) {
// No enabled MQTT controller
return false;
}
// Do nothing if already connected
if (MQTTclient_037->connected())return true;
if (MQTTclient_037->connected()) return true;
// define stuff for the client - this could also be done in the intial declaration of MQTTclient_037
if (WiFi.status() != WL_CONNECTED) {
return false; // Not connected, so no use in wasting time to connect to a host.
}
ControllerSettingsStruct ControllerSettings;
LoadControllerSettings(enabledMqttController, (byte*)&ControllerSettings, sizeof(ControllerSettings));
if (ControllerSettings.UseDNS) {
MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port);
} else {
@@ -352,8 +354,8 @@ boolean MQTTConnect_037(String clientid)
{
String log = "";
if ((SecuritySettings.ControllerUser[0][0] != 0) && (SecuritySettings.ControllerPassword[0][0] != 0)) //
result = MQTTclient_037->connect(clientid.c_str(), SecuritySettings.ControllerUser[0], SecuritySettings.ControllerPassword[0]); // todo
if ((SecuritySettings.ControllerUser[enabledMqttController][0] != 0) && (SecuritySettings.ControllerPassword[enabledMqttController][0] != 0))
result = MQTTclient_037->connect(clientid.c_str(), SecuritySettings.ControllerUser[enabledMqttController], SecuritySettings.ControllerPassword[enabledMqttController]);
else
result = MQTTclient_037->connect(clientid.c_str());
@@ -453,20 +455,19 @@ float string2float(String myString) {
len = myString.length();
char tmp[(len + 1)]; // one extra for the zero termination
byte start = 0;
// Look for decimal point - they can be anywhere but no more than one of them!
int dotIndex = myString.indexOf(".");
int dotIndex = myString.indexOf('.');
//Serial.println(dotIndex);
if (dotIndex != -1)
{
int dotIndex2 = (myString.substring(dotIndex + 1)).indexOf(".");
int dotIndex2 = (myString.substring(dotIndex + 1)).indexOf('.');
//Serial.println(dotIndex2);
if (dotIndex2 != -1)return -999.00; // Give error if there is more than one dot
}
if (myString.substring(0, 1) == "-") {
if (myString.charAt(0) == '-') {
tmp[0] = '-';
start = 1; //allow a minus in front of string
}
+1
View File
@@ -153,6 +153,7 @@ boolean Plugin_044(byte function, struct EventStruct *event, String& string)
{
if (P1GatewayServer) {
P1GatewayServer->close();
//FIXME: shouldnt P1P1GatewayServer be deleted?
P1GatewayServer = NULL;
}
success = true;
+6 -5
View File
@@ -109,11 +109,12 @@ boolean Plugin_056(byte function, struct EventStruct *event, String& string)
case PLUGIN_EXIT:
{
//FIXME: if this plugin is used more than once at the same time, things go horribly wrong :)
if (Plugin_056_SDS)
delete Plugin_056_SDS;
addLog(LOG_LEVEL_INFO, F("SDS : Exit"));
// //FIXME: if this plugin is used more than once at the same time, things go horribly wrong :)
//
// if (Plugin_056_SDS)
// delete Plugin_056_SDS;
// addLog(LOG_LEVEL_INFO, F("SDS : Exit"));
shouldReboot=true;
break;
}
+14 -9
View File
@@ -1,4 +1,5 @@
nodes=[
# node 0
{
'type' : 'wemos d1 mini v2.2.0',
'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.1:1.0-port0',
@@ -7,6 +8,7 @@ nodes=[
'build_cmd' : 'platformio run --environment dev_ESP8266_4096'
},
# node 1
{
'type' : 'nodemcu geekcreit ESP12E devkit v2',
'port' : '/dev/serial/by-path/pci-0000:00:14.0-usb-0:3.2:1.0-port0',
@@ -15,20 +17,23 @@ nodes=[
'build_cmd' : 'platformio run --environment dev_ESP8266_4096'
},
{
'node' : 3,
'type' : 'wemos d1 mini v2.2.0',
'port' : '/dev/ttyUSB0',
'ip' : '192.168.13.91',
'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_ESP8266_4096/firmware.bin --flash_size=32m -p',
'build_cmd' : 'platformio run --environment dev_ESP8266_4096'
},
# {
# 'node' : 3,
# 'type' : 'wemos d1 mini v2.2.0',
# 'port' : '/dev/ttyUSB0',
# 'ip' : '192.168.13.91',
# 'flash_cmd' : 'esptool.py --port {port} -b 1500000 write_flash 0x0 .pioenvs/dev_ESP8266_4096/firmware.bin --flash_size=32m -p',
# 'build_cmd' : 'platformio run --environment dev_ESP8266_4096'
# },
]
#an mqtt broker that both ESPEasy and the test suite are connecting to
mqtt_broker="192.168.13.236"
mqtt_port=1883
#ip of the server running this script
http_server="192.168.13.236"
test_server="192.168.13.236"
http_port=8080
linebased_port=8181
+244
View File
@@ -0,0 +1,244 @@
from espcore import *
import json
import threading
from queue import Queue
from logging import getLogger
import time
import re
SENSOR_TYPE_SINGLE = 1
SENSOR_TYPE_TEMP_HUM = 2
SENSOR_TYPE_TEMP_BARO = 3
SENSOR_TYPE_TEMP_HUM_BARO = 4
SENSOR_TYPE_DUAL = 5
SENSOR_TYPE_TRIPLE = 6
SENSOR_TYPE_QUAD = 7
SENSOR_TYPE_SWITCH = 10
SENSOR_TYPE_DIMMER = 11
SENSOR_TYPE_LONG = 20
SENSOR_TYPE_WIND = 21
class ControllerEmu:
"""class that emulates and decodes various types of controllers. run various threads in the background to recveive and queue stuff"""
def __init__(self):
self.log=logging.getLogger("controller")
self.start_mqtt()
self.start_http()
self.start_linebased()
def start_mqtt(self):
"""generic mqtt receiver. just queues all reqeived mqtt messages on all topics"""
getLogger("mqtt").debug("Connecting to {mqtt_broker}".format(mqtt_broker=config.mqtt_broker))
import paho.mqtt.client as mqtt
mqtt_client = mqtt.Client()
mqtt_client.connect(config.mqtt_broker, 1883, 60)
mqtt_client.loop_start()
mqtt_client.subscribe('#')
self.mqtt_messages=Queue()
def mqtt_on_message(client, userdata, message):
logging.getLogger("mqtt").debug("Received message '" + str(message.payload) + "' on topic '"
+ message.topic + "' with QoS " + str(message.qos))
self.mqtt_messages.put(message)
mqtt_client.on_message=mqtt_on_message
def clear_mqtt(self):
"""clear queue"""
time.sleep(1)
while not self.mqtt_messages.empty():
self.mqtt_messages.get()
def start_http(self):
"""generic http receiver. just queues all shallow copy of all http requests objects"""
import bottle
self.http_requests = Queue()
@bottle.post('<filename:path>')
@bottle.get('<filename:path>')
def urlhandler(filename):
logging.getLogger("http").debug(bottle.request.method+" "+str(dict(bottle.request.params)))
self.http_requests.put(bottle.request.copy())
http_thread=threading.Thread(target=bottle.run, kwargs=dict(host='0.0.0.0', port=config.http_port, reloader=False))
http_thread.daemon=True
http_thread.start()
def clear_http(self):
"""clear queue"""
while not self.http_requests.empty():
self.http_requests.get()
def start_linebased(self):
"""generic linebased receiver (like telnet). for nodo plugin"""
import socket
import sys
self.linebased_lines=Queue()
def handle_connect(connection, client_address):
logging.getLogger("linebased").debug("Connect from "+str(client_address))
fh = connection.makefile()
for line in fh:
line=line.rstrip()
logging.getLogger("linebased").debug("Recv from "+str(client_address)+" :"+line)
self.linebased_lines.put( ( client_address, line ) )
logging.getLogger("linebased").debug("Disconnect from "+str(client_address))
def wait_accept():
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('', config.linebased_port)
sock.bind(server_address)
sock.listen()
while True:
# Wait for a connection
connection, client_address = sock.accept()
connect_thread=threading.Thread(target=handle_connect, kwargs=dict(connection=connection, client_address=client_address))
connect_thread.daemon=True
connect_thread.start()
accept_thread=threading.Thread(target=wait_accept)
accept_thread.daemon=True
accept_thread.start()
def clear_linebased(self):
"""clear queue"""
while not self.linebased_lines.empty():
self.linebased_lines.get()
def clear(self, sleep=0):
self.clear_http()
self.clear_mqtt()
self.clear_linebased()
time.sleep(sleep)
def recv_domoticz_http(self, sensor_type, idx, timeout=60):
"""recv a domoticz http request from espeasy, and convert back to espeasy values"""
start_time=time.time()
self.log.info("Waiting for domoticz http request idx {idx} with sensortype {sensor_type}".format(sensor_type=sensor_type,idx=idx))
# self.clear_http()
# read and parse http requests
while time.time()-start_time<timeout:
request=self.http_requests.get(block=True, timeout=timeout)
if request.path == "/json.htm":
if int(request.params.get('idx'))==idx:
if request.params.get('param')=='udevice':
# svalues_str=request.params.get('svalue').split(";")
#nooozzz..the ; isnt properly escaped so python only returns the first value. woraround:
svalues_str=re.findall('svalue=([0-9.;]*)',request.query_string)[0].split(";")
svalues=[]
for svalue in svalues_str:
try:
f=float(svalue)
except:
f=0
svalues.append(f)
if ( sensor_type==SENSOR_TYPE_SINGLE or sensor_type==SENSOR_TYPE_LONG ) and len(svalues)==1:
return svalues
elif sensor_type==SENSOR_TYPE_DUAL and len(svalues)==2:
return svalues
elif sensor_type==SENSOR_TYPE_TEMP_HUM and len(svalues)==3:
return [svalues[0], svalues[1]]
elif sensor_type==SENSOR_TYPE_TEMP_BARO and len(svalues)==5:
return [svalues[0], svalues[3]]
elif sensor_type==SENSOR_TYPE_TRIPLE and len(svalues)==3:
return svalues
elif sensor_type==SENSOR_TYPE_TEMP_HUM_BARO and len(svalues)==5:
return [svalues[0],svalues[1], svalues[3]]
elif sensor_type==SENSOR_TYPE_QUAD and len(svalues)==4:
return svalues
elif sensor_type==SENSOR_TYPE_WIND and len(svalues)==6:
return [svalues[0], svalues[2]/10, svalues[3]/10]
elif request.params.get('param')=='switchlight':
if sensor_type==SENSOR_TYPE_DIMMER or sensor_type == SENSOR_TYPE_SWITCH:
if request.params.get('switchcmd') == 'Off':
return [0]
elif request.params.get('switchcmd') == 'On':
return [1]
elif request.params.get('switchcmd') == 'Set Level':
return [float(request.params.get('level'))]
raise(Exception("Timeout"))
def recv_domoticz_mqtt(self, sensor_type, idx, timeout=60):
"""recv a domoticz mqtt request from espeasy, and convert back to espeasy values"""
start_time=time.time()
self.log.info("Waiting for domoticz mqtt request idx {idx} with sensortype {sensor_type}".format(sensor_type=sensor_type,idx=idx))
# self.clear_mqtt()
# read and parse mqtt requests
while time.time()-start_time<timeout:
message=self.mqtt_messages.get(block=True, timeout=timeout)
if message.topic == "domoticz/in":
#decode domoticz json (should be valid json! otherwise there is a bug)
params=json.loads(message.payload.decode())
if int(params.get('idx'))==idx:
if 'command' not in params:
svalues_str=params.get('svalue').split(";")
svalues=[]
for svalue in svalues_str:
try:
f=float(svalue)
except:
f=0
svalues.append(f)
if ( sensor_type==SENSOR_TYPE_SINGLE or sensor_type==SENSOR_TYPE_LONG ) and len(svalues)==1:
return svalues
elif sensor_type==SENSOR_TYPE_DUAL and len(svalues)==2:
return svalues
elif sensor_type==SENSOR_TYPE_TEMP_HUM and len(svalues)==3:
return [svalues[0], svalues[1]]
elif sensor_type==SENSOR_TYPE_TEMP_BARO and len(svalues)==5:
return [svalues[0], svalues[3]]
elif sensor_type==SENSOR_TYPE_TRIPLE and len(svalues)==3:
return svalues
elif sensor_type==SENSOR_TYPE_TEMP_HUM_BARO and len(svalues)==5:
return [svalues[0],svalues[1], svalues[3]]
elif sensor_type==SENSOR_TYPE_QUAD and len(svalues)==4:
return svalues
elif sensor_type==SENSOR_TYPE_WIND and len(svalues)==6:
return [svalues[0], svalues[2]/10, svalues[3]/10]
elif params.get('command')=='switchlight':
if sensor_type==SENSOR_TYPE_DIMMER or sensor_type == SENSOR_TYPE_SWITCH:
if params.get('switchcmd') == 'Off':
return [0]
elif params.get('switchcmd') == 'On':
return [1]
elif params.get('Set%20Level'):
return [int(params.get('Set%20Level'))]
raise(Exception("Timeout while expecting mqtt json message"))
+35 -25
View File
@@ -20,6 +20,9 @@ Examples:
Build, flash and connect to serial for node 1:
./espcli node 1 bfs
Build and flash all configured nodes:
./espcli node all bf
Configure wifi settings for node:
./espcli node 0 wificonfig
@@ -43,43 +46,50 @@ parser = argparse.ArgumentParser(description='ESPEasy testing framework', epilog
parser.add_argument('--debug', action='store_true', help='enable http debugging output')
parser.add_argument('module', nargs=1, help='Module to use (node or espeasy)')
parser.add_argument('node', type=int,nargs=1, help='Node number (from config)')
node_choices=['all']
for node_choice in range(0,len(config.nodes)):
node_choices.append(str(node_choice))
parser.add_argument('node', choices=node_choices,nargs=1, help='Node number (from config)')
parser.add_argument('command', nargs=1, help='Function from the module (help below)')
parser.add_argument('params', default=[], nargs='*', help='command parameters. in foo=bar form ')
args = parser.parse_args()
try:
if args.node[0]<0:
raise(Exception("node number should be 0 or higher"))
if args.debug:
util.enable_http_debug()
node_index=args.node[0]
#we always need a node instance
node_instance=node.Node(config.nodes[node_index], "node"+str(node_index))
#determine rpc parameters
params={}
for param in args.params:
(key,value)=param.split("=")
params[key]=value
if args.module[0]=='node':
command = getattr(node_instance, args.command[0])
command(**params)
elif args.module[0]=='espeasy':
espeasy_instance=espeasy.EspEasy(node_instance)
command = getattr(espeasy_instance, args.command[0])
command(**params)
#which node to process? all or a specific one?
if args.node[0]=='all':
node_indexes=range(0,len(config.nodes))
else:
raise(Exception("Unknown module:"+args.module[0]))
node_indexes=[int(args.node[0])]
#traverse selected nodes
for node_index in node_indexes:
node_instance=node.Node(config.nodes[node_index], "node"+str(node_index))
#determine rpc parameters
params={}
for param in args.params:
(key,value)=param.split("=")
params[key]=value
if args.module[0]=='node':
command = getattr(node_instance, args.command[0])
command(**params)
elif args.module[0]=='espeasy':
espeasy_instance=espeasy.EspEasy(node_instance)
command = getattr(espeasy_instance, args.command[0])
command(**params)
else:
raise(Exception("Unknown module:"+args.module[0]))
sys.exit(0)
+2 -93
View File
@@ -1,103 +1,12 @@
### low level logging and protocol handling
# normally you shouldnt need to look into this file too much
import logging
import colorlog
import config
import paho.mqtt.client as mqtt
import json
import bottle
import threading
from queue import Queue, Empty
colorlog.basicConfig(level=logging.DEBUG)
logging.getLogger("requests.packages.urllib3.connectionpool").setLevel(logging.ERROR)
### mqtt stuff
logging.getLogger("MQTT").debug("Connecting to {mqtt_broker}".format(mqtt_broker=config.mqtt_broker))
mqtt_client = mqtt.Client()
mqtt_client.connect(config.mqtt_broker, 1883, 60)
mqtt_client.loop_start()
mqtt_client.subscribe('#')
mqtt_messages=[]
def mqtt_on_message(client, userdata, message):
logging.getLogger("MQTT").debug("Received message '" + str(message.payload) + "' on topic '"
+ message.topic + "' with QoS " + str(message.qos))
mqtt_messages.append(message)
mqtt_client.on_message=mqtt_on_message
def mqtt_expect_json(topic, matches, timeout=60):
"""wait until a specific json message is received, and return it decoded. ignores all other messages"""
start_time=time.time()
logging.getLogger("MQTT").info("Waiting for json message on topic {topic}, with values {matches}".format(topic=topic, matches=matches))
# check mqtt results
while time.time()-start_time<timeout:
while mqtt_messages:
message=mqtt_messages.pop()
try:
#ignore decoding exceptions
payload=json.loads(message.payload.decode())
except:
continue
if message.topic == topic:
ok=True
for match in matches.items():
if not match[0] in payload or payload[match[0]]!=match[1]:
ok=False
if ok:
return(payload)
time.sleep(1)
raise(Exception("Timeout while expecting mqtt json message"))
### http server stuff.
# the http server just accepts everything and stores it in the http_requests queue
import bottle
http_requests = Queue()
@bottle.post('<filename:path>')
@bottle.get('<filename:path>')
def urlhandler(filename):
logging.getLogger("HTTP").debug(bottle.request.method+" "+str(dict(bottle.request.params)))
http_requests.put(bottle.request.copy())
http_thread=threading.Thread(target=bottle.run, kwargs=dict(host='0.0.0.0', port=config.http_port, reloader=False))
http_thread.daemon=True
http_thread.start()
def http_expect_request(path, matches, timeout=60):
"""wait until a specific path and paraters are request on the http server. ignores all other requests"""
start_time=time.time()
logging.getLogger("HTTP").info("Waiting for http request on path {path}, with values {matches}".format(path=path, matches=matches))
# check http results
while time.time()-start_time<timeout:
while True:
request=http_requests.get(block=True, timeout=timeout)
# logging.getLogger("HTTP").debug("Body: "+str(request.body.str))
if request.path == path:
ok=True
for match in matches.items():
if not match[0] in request.params or request.params[match[0]]!=match[1]:
ok=False
if ok:
return(request)
time.sleep(1)
raise(Exception("Timeout while expecting http message"))
+167 -152
View File
@@ -4,20 +4,8 @@
from espcore import *
import time
SENSOR_TYPE_SINGLE = 1
SENSOR_TYPE_TEMP_HUM = 2
SENSOR_TYPE_TEMP_BARO = 3
SENSOR_TYPE_TEMP_HUM_BARO = 4
SENSOR_TYPE_DUAL = 5
SENSOR_TYPE_TRIPLE = 6
SENSOR_TYPE_QUAD = 7
SENSOR_TYPE_SWITCH = 10
SENSOR_TYPE_DIMMER = 11
SENSOR_TYPE_LONG = 20
SENSOR_TYPE_WIND = 21
class EspEasy:
@@ -37,45 +25,42 @@ class EspEasy:
)
def controller_domoticz_mqtt(self, **kwargs):
"""config controller to use domoticz via mqtt"""
self._node.log.info("Configuring controller domoticz mqtt "+str(kwargs))
def post_controller(self, index, data):
"""post controller form to espeasy"""
self._node.http_post(
twice=True, # needed for controllers and devices because of the way its implemented
page="controllers",
params="""
index:{index}
""".format(**kwargs),
""".format(index=index),
data="""
data=data
)
def controller_domoticz_mqtt(self, index=1, controllerip=config.mqtt_broker, controllerport=config.mqtt_port, **kwargs):
"""config controller to use domoticz via mqtt"""
self._node.log.info("Configuring controller domoticz mqtt "+str(kwargs))
self.post_controller(index,"""
protocol:2
usedns:0
controllerip:{controllerip}
controllerport:1883
controllerport:{controllerport}
controlleruser:
controllerpassword:
controllersubscribe:domoticz/out
controllerpublish:domoticz/in
controllerenabled:on
""".format(**kwargs)
""".format(controllerip=controllerip, controllerport=controllerport, **kwargs)
)
def controller_domoticz_http(self, **kwargs):
def controller_domoticz_http(self, index=1, controllerip=config.test_server, controllerport=config.http_port, **kwargs):
"""config controller to use domoticz via http"""
self._node.log.info("Configuring controller domoticz http "+str(kwargs))
self._node.http_post(
twice=True, # needed for controllers and devices because of the way its implemented
page="controllers",
params="""
index:{index}
""".format(**kwargs),
data="""
self.post_controller(index,"""
protocol:1
usedns:0
controllerip:{controllerip}
@@ -83,63 +68,29 @@ class EspEasy:
controlleruser:
controllerpassword:
controllerenabled:on
""".format(**kwargs)
""".format(controllerip=controllerip, controllerport=controllerport, **kwargs)
)
def recv_domoticz_http(self, sensor_type, timeout=60):
"""recv a domoticz http request from espeasy, and convert back to espeasy values"""
start_time=time.time()
logging.getLogger("domoticz http").info("Waiting for request with sensortype {sensor_type}".format(sensor_type=sensor_type))
def controller_nodo(self, index=1,controllerip=config.test_server, controllerport=config.linebased_port, **kwargs):
"""config controller to use nodo"""
# read and parse http requests
while time.time()-start_time<timeout:
request=http_requests.get(block=True, timeout=timeout)
if request.path == "/json.html":
if reuqest.params.get('param')=='udevice':
svalues=request.params.get('svalue').split(";")
if sensor_type==SENSOR_TYPE_SINGLE and len(svalues)==1:
return svalues
elif sensor_type==SENSOR_TYPE_DUAL and len(svalues)==2:
return svalues
elif sensor_type==SENSOR_TYPE_HUM and len(svalues)==3:
return svalues
elif sensor_type==SENSOR_TYPE_BARO and len(svalues)==5:
return [svalues[0], svalues[3]]
elif sensor_type==SENSOR_TYPE_TRIPLE and len(svalues)==3:
return svalues
elif sensor_type==SENSOR_TYPE_HUM_BARO and len(svalues)==5:
return [svalues[0],svalues[2], svalues[3]]
elif sensor_type==SENSOR_TYPE_QUAD and len(svalues)==4:
return svalues
elif sensor_type==SENSOR_TYPE_WIND and len(svalues)==5:
return [svalues[0], svalues[2], svalues[3]]
elif request.params.get('param')=='switchlight':
if sensor_type==SENSOR_TYPE_DIMMER or sensor_type == SENSOR_TYPE_SWITCH:
if request.params.get('switchcmd') == 'Off':
return [0]
elif request.params.get('switchcmd') == 'On':
return [1]
elif request.params.get('switchcmd') == 'Set Level':
return [request.params.get('level')]
raise(Exception("Timeout"))
self._node.log.info("Configuring controller nodo "+str(kwargs))
self.post_controller(index,"""
protocol:3
usedns:0
controllerip:{controllerip}
controllerport:{controllerport}
controllerpassword:
controllerenabled:on
""".format(controllerip=controllerip, controllerport=controllerport,**kwargs)
)
def controller_thingspeak(self, **kwargs):
def controller_thingspeak(self, index=1, **kwargs):
self._node.log.info("Configuring controller thingspeak "+str(kwargs))
self._node.http_post(
twice=True, # needed for controllers and devices because of the way its implemented
page="controllers",
params="""
index:{index}
""".format(**kwargs),
data="""
self.post_controller(index,"""
protocol:4
usedns:0
controllerip:{controllerip}
@@ -151,97 +102,161 @@ class EspEasy:
)
def device_p001(self, **kwargs):
self._node.log.info("Config device plugin p001 "+str(kwargs))
def post_device(self, index, data):
"""post a device form to espeasy"""
self._node.log.info("Configuring tasknumber {index}".format(index=index))
self._node.http_post(
twice=True, # needed for controllers and devices because of the way its implemented
page="devices",
params="""
index:{index}
""".format(**kwargs),
data="""
TDNUM:1
TDN:
TDE:on
taskdevicepin1:{taskdevicepin1}
plugin_001_type:{plugin_001_type}
plugin_001_button:{plugin_001_button}
TDSD1:on
TDID1:{TDID1}
TDT:0
TDVN1:Switch
edit:1
""".format(**kwargs)
""".format(index=index),
data=data
)
# def device_p001(self, index, **kwargs):
# self._node.log.info("Config device plugin p001 "+str(kwargs))
#
# self.post_device(index, """
# TDNUM:1
# TDN:
# TDE:on
# taskdevicepin1:{taskdevicepin1}
# plugin_001_type:{plugin_001_type}
# plugin_001_button:{plugin_001_button}
# TDSD1:on
# TDID1:{TDID1}
# TDT:0
# TDVN1:Switch
# edit:1
# """.format(**kwargs)
# )
def device_p004(self, **kwargs):
self._node.log.info("Config device plugin p004 "+str(kwargs))
self._node.http_post(
twice=True, # needed for controllers and devices because of the way its implemented
page="devices",
# def device_p004(self, **kwargs):
# self._node.log.info("Config ds18b20 with "+str(kwargs))
#
# self._node.http_post(
# twice=True, # needed for controllers and devices because of the way its implemented
# page="devices",
#
# params="""
# index:{index}
# """.format(**kwargs),
#
# data="""
# TDNUM:4
# TDN:temp
# TDE:on
# taskdevicepin1:{taskdevicepin1}
# plugin_004_dev:{plugin_004_dev}
# plugin_004_res:{plugin_004_res}
# TDT:5
# TDVN1:Temperature
# TDF1:
# TDVD1:2
# TDSD1:on
# TDID1:{TDID1}
# edit:1
# page:1
# """.format(**kwargs)
# )
params="""
index:{index}
""".format(**kwargs),
# def device_p005(self, **kwargs):
# self._node.log.info("Config DHT22 on D3 with "+str(kwargs))
#
# self._node.http_post(
# twice=True, # needed for controllers and devices because of the way its implemented
# page="devices",
#
# params="""
# index:{index}
# """.format(**kwargs),
#
# data="""
# TDNUM:5
# TDN:
# TDE:on
# taskdevicepin1:0
# plugin_005_dhttype:22
# TDSD1:on
# TDID1:{TDID1}
# TDT:5
# TDVN1:Temperature
# TDF1:
# TDVD1:2
# TDVN2:Humidity
# TDF2:
# TDVD2:2
# edit:1
# page:1
# """.format(**kwargs)
# )
def device_p033(self, index, **kwargs):
self._node.log.info("Config dummy device "+str(kwargs))
self.post_device(index=index,
data="""
TDNUM:4
TDN:temp
TDE:on
taskdevicepin1:{taskdevicepin1}
plugin_004_dev:{plugin_004_dev}
plugin_004_res:{plugin_004_res}
TDT:5
TDVN1:Temperature
TDF1:
TDVD1:2
TDSD1:on
TDID1:{TDID1}
edit:1
page:1
""".format(**kwargs)
)
def device_p036(self, **kwargs):
self._node.log.info("Config device plugin p036 "+str(kwargs))
self._node.http_post(
twice=True, # needed for controllers and devices because of the way its implemented
page="devices",
params="""
index:{index}
""".format(**kwargs),
data="""
TDNUM:36
TDNUM:33
TDN:
TDE:on
plugin_036_adr:60
plugin_036_rotate:1
plugin_036_nlines:1
plugin_036_scroll:1
Plugin_036_template1:espeasy
Plugin_036_template2:test
Plugin_036_template3:suite
Plugin_036_template4:test1
Plugin_036_template5:test2
Plugin_036_template6:test3
Plugin_036_template7:test4
Plugin_036_template8:test5
Plugin_036_template9:test6
Plugin_036_template10:test7
Plugin_036_template11:test8
Plugin_036_template12:test9
taskdevicepin3:-1
plugin_036_timer:0
plugin_033_sensortype:{plugin_033_sensortype}
TDSD1:on
TDID1:{TDID1}
TDT:1
TDVN1:first
TDVD1:2
TDVN2:second
TDVD2:2
TDVN3:third
TDVD3:2
TDVN4:fourth
TDVD4:2
edit:1
page:1
""".format(**kwargs)
)
# def device_p036(self, **kwargs):
# self._node.log.info("Config framed oled p036 with "+str(kwargs))
#
# self._node.http_post(
# twice=True, # needed for controllers and devices because of the way its implemented
# page="devices",
#
# params="""
# index:{index}
# """.format(**kwargs),
#
# data="""
# TDNUM:36
# TDN:
# TDE:on
# plugin_036_adr:60
# plugin_036_rotate:1
# plugin_036_nlines:1
# plugin_036_scroll:1
# Plugin_036_template1:espeasy
# Plugin_036_template2:test
# Plugin_036_template3:suite
# Plugin_036_template4:test1
# Plugin_036_template5:test2
# Plugin_036_template6:test3
# Plugin_036_template7:test4
# Plugin_036_template8:test5
# Plugin_036_template9:test6
# Plugin_036_template10:test7
# Plugin_036_template11:test8
# Plugin_036_template12:test9
# taskdevicepin3:-1
# plugin_036_timer:0
# TDT:1
# edit:1
# page:1
# """.format(**kwargs)
# )
+109 -1
View File
@@ -2,20 +2,128 @@
#in each test just do: from esptest import *
#look in this file for basic functions to use in your tests
# from espeasy import *
from node import *
from espeasy import *
from controlleremu import *
import config
import time
import shelve
import os
from espcore import *
### create node objects and espeasy objects
node=[]
espeasy=[]
for n in config.nodes:
node.append(Node(n, "node"+str(len(node))))
espeasy.append(EspEasy(node[-1]))
# steps=[]
log=logging.getLogger("esptest")
## controller emulators
controller=ControllerEmu()
### keep test state, so we can skip tests.
global state
state={
'module': None,
'name': None,
'title':""
}
with shelve.open("test.state") as shelve_db:
if 'state' in shelve_db:
state=shelve_db['state']
def step(title=""):
def step_dec(test):
"""add test step. test can resume from every test-step"""
if state['module'] and ( state['module'] != test.__module__ or state['name'] != test.__name__ or state['title'] != title):
log.debug("Skipping step "+title+": "+test.__module__ + "." + test.__name__ )
else:
state['module']=None
print()
log.info("*** Starting step "+title+": "+test.__module__ + "." + test.__name__ )
# store this test so we may resume later
with shelve.open("test.state") as shelve_db:
shelve_db['state']={
'module': test.__module__,
'name': test.__name__,
'title': title
}
#run the test. if there is an exception we resume this test the next time
test()
log.info("*** Starting step "+title+": "+test.__module__ + "." + test.__name__ )
return(step_dec)
# ### run all the tests
# def run():
# """run all tests"""
#
# for step in steps:
# print()
# log.info("*** Starting "+step.title+": "+step.__module__ + "." + step.__name__ )
#
# # store this step so we may resume later
# state['module']=step.__module__
# state['name']=step.__name__
# state['title']=step.title
# with shelve.open("test.state") as shelve_db:
# shelve_db['state']=state
#
# #run the step. if there is an exception we resume this step the next time
# step()
#
#
# # log.info("Completed step")
#
# # all Completed
# os.unlink("test.state")
# log.info("*** All tests complete ***")
### auxillary test functions
def test_in_range(value, min, max):
if value < min or value > max:
raise(Exception("Value {value} should be between {min} and {max}".format(value=value, min=min, max=max)))
log.info("OK: value {value} is between {min} and {max}".format(value=value, min=min, max=max))
def test_is(value, shouldbe):
if value!=shouldbe:
raise(Exception("Value {value} should be {shouldbe}".format(value=value, shouldbe=shouldbe)))
log.info("OK: Value is {value}".format(value=value, shouldbe=shouldbe))
def pause(seconds):
log.info("Waiting for {seconds} seconds".format(seconds=seconds))
time.sleep(seconds)
def completed():
if os.path.exists("test.state"):
os.unlink("test.state")
log.info("*** All tests completed ***")
+66 -7
View File
@@ -54,11 +54,52 @@ class Node():
def reboot(self):
'''reboot the esp via the serial DTR line'''
self.log.debug("Rebooting")
self.serial_needed()
self._serial.setDTR(0)
time.sleep(0.1)
self._serial.setDTR(1)
def powercycle(self):
"""powercycle the device"""
self.poweroff()
self.poweron()
def poweroff(self):
"""power off device"""
#cant yet be done automaticly unfortunatly
self.log.info("Please power off node "+self._id)
done=False
while not done:
try:
self.serial_needed()
self._serial.readline()
except serial.SerialException:
done=True
if hasattr(self, '_serial'):
del self._serial
self.log.debug("Detected power off")
def poweron(self):
"""power on device"""
self.log.info("Please power on node "+self._id)
done=False
while not done:
try:
self.serial_needed()
self._serial.readline()
done=True
except serial.SerialException:
time.sleep(0.1)
self.log.debug("Detected power on")
def pingwifi(self, timeout=60):
"""waits until espeasy reponds via wifi"""
@@ -91,6 +132,15 @@ class Node():
self.pingwifi(timeout=timeout)
def serialcmd(self, command):
"""send command via serial"""
self.serial_needed()
self.log.debug("Send serial command: "+command)
serial_str=command+"\n"
self._serial.write(bytes(serial_str, 'ascii'));
def build(self):
"""compile binary"""
@@ -132,18 +182,25 @@ class Node():
self.flashserial()
self.serial()
def bf(self):
"""build + flashserial"""
self.build()
self.flashserial()
def http_post(self, page, params, data=None, twice=False):
def http_post(self, page, params=None, data=None, twice=False):
"""http post to espeasy webinterface. (GET if data is None)"""
# transform easy copy/pastable chromium data into a dict
params_dict={}
for line in params.split("\n"):
m=re.match(" *(.*?):(.*)",line)
if (m):
params_dict[m.group(1)]=m.group(2)
if params:
params_dict={}
for line in params.split("\n"):
m=re.match(" *(.*?):(.*)",line)
if (m):
params_dict[m.group(1)]=m.group(2)
else:
params_dict=None
if data:
data_dict={}
@@ -155,9 +212,11 @@ class Node():
data_dict=None
url=self._url+page
self.log.debug("HTTP POST {url} with params {params} and data {data}".format(url=url,params=params,data=data))
r=requests.post(
self._url+page,
url,
params=params_dict,
data=data_dict
)
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - node 1
# - D6 connected to eachother
# tests:
# - pulsing the node0 pin and picking it up on node1 which sends it via domoticz mqtt
espeasy[1].controller_domoticz_mqtt(index=1, controllerip=config.mqtt_broker)
espeasy[1].device_p001(index=1, taskdevicepin1=12, plugin_001_type=1, plugin_001_button=0, TDID1=1415)
### test
espeasy[0].control(cmd="gpio,12,1")
time.sleep(1)
espeasy[0].control(cmd="gpio,12,0")
time.sleep(1)
# check mqtt results
for message in mqtt_messages:
payload=json.loads(message.payload.decode())
if message.topic == 'domoticz/in' and payload['idx']==1415 and payload['switchcmd']=='Off' and payload['command']=='switchlight':
log.info("all tests ok")
sys.exit(0)
#FIXME: this need to be callable, to run a batch of tests at once
raise(Exception("Test failed"))
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - node 1
# - D6 connected to eachother
# tests:
# - pulsing the node0 pin and picking it up on node1 which sends it via domoticz mqtt
@step()
def prepare():
node[0].reboot()
node[0].pingserial()
node[0].serialcmd("resetFlashWriteCounter")
node[1].reboot()
node[1].pingserial()
node[1].serialcmd("resetFlashWriteCounter")
espeasy[1].controller_domoticz_mqtt()
espeasy[1].post_device(1, """
TDNUM:1
TDN:
TDE:on
taskdevicepin1:12
plugin_001_type:1
plugin_001_button:0
TDSD1:on
TDID1:1000
TDT:0
TDVN1:Switch
edit:1
""")
@step()
def test():
espeasy[0].control(cmd="gpio,12,1")
pause(5)
controller.clear_mqtt()
espeasy[0].control(cmd="gpio,12,0")
values=controller.recv_domoticz_mqtt(SENSOR_TYPE_SWITCH,1000)
test_is(values[0],0)
if __name__=='__main__':
completed()
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - 2x ds18b12 connected to D4
# tests:
# - correct values are send to domoticz via http
# - correct values are send to domoticz via mqtt
# espeasy[0].controller_thingspeak(index=1, controllerip=config.http_server, controllerport=config.http_port)
### config mqtt and the 2 devices
# espeasy[0].controller_domoticz_http(index=1, controllerip=config.http_server, controllerport=config.http_port)
# espeasy[0].device_p004(index=1, taskdevicepin1=2, plugin_004_dev=0, plugin_004_res=9, TDID1=1417)
# espeasy[0].device_p004(index=2, taskdevicepin1=2, plugin_004_dev=1, plugin_004_res=9, TDID1=1418)
print(espeasy[0].recv_domoticz_http(SENSOR_TYPE_SINGLE))
exit
#
#wait for request
temp1=http_expect_request("/update",{ 'apikey': 'thingspeakkey1234'}, timeout=10)
temp1=http_expect_request("/json.htm", { 'idx': '1417' }, timeout=10)
temp2=http_expect_request("/json.htm", { 'idx': '1418' }, timeout=10)
if not ( float(temp1.params['svalue'])>0 and float(temp1.params['svalue'])<40 and float(temp2.params['svalue'])>0 and float(temp2.params['svalue'])<40):
raise(Exception("Wrong results"))
### reconfig for mqtt and expect results
espeasy[0].controller_domoticz_mqtt(index=1, controllerip=config.mqtt_broker)
temp1=mqtt_expect_json("domoticz/in", { 'idx': 1417 }, timeout=10)
temp2=mqtt_expect_json("domoticz/in", { 'idx': 1418 }, timeout=10)
if not ( float(temp1['svalue'])>0 and float(temp1['svalue'])<40 and float(temp2['svalue'])>0 and float(temp2['svalue'])<40):
raise(Exception("Wrong results"))
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - 2x ds18b12 connected to D4
# tests:
# - correct values are send to domoticz via mqtt
# - test if powercycle value is valid (https://github.com/letscontrolit/ESPEasy/issues/719)
@step()
def prepare():
node[0].reboot()
node[0].pingserial()
node[0].serialcmd("resetFlashWriteCounter")
espeasy[0].controller_domoticz_mqtt()
espeasy[0].post_device(index=1, data="""
TDNUM:4
TDN:temp
TDE:on
taskdevicepin1:2
plugin_004_dev:0
plugin_004_res:9
TDT:5
TDVN1:Temperature
TDF1:
TDVD1:2
TDSD1:on
TDID1:2001
edit:1
page:1
""")
espeasy[0].post_device(index=2, data="""
TDNUM:4
TDN:temp
TDE:on
taskdevicepin1:2
plugin_004_dev:1
plugin_004_res:9
TDT:5
TDVN1:Temperature
TDF1:
TDVD1:2
TDSD1:on
TDID1:2002
edit:1
page:1
""")
@step()
def test():
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_SINGLE,2001)
test_in_range(results[0], -5,40)
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_SINGLE,2002)
test_in_range(results[0], -5,40)
@step()
def powercycle():
"""test result on poweron"""
node[0].powercycle()
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_SINGLE,2001)
test_in_range(results[0], -5,40)
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_SINGLE,2002)
test_in_range(results[0], -5,40)
if __name__=='__main__':
completed()
-12
View File
@@ -1,12 +0,0 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - framed oled display connected to default I2C
# tests:
# - only configures it, nothing is verfied yet
espeasy[0].device_p036(index=1)
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - framed oled display connected to default I2C
# tests:
# - only configures it, nothing is verfied yet
espeasy[0].post_device(1, data="""
TDNUM:36
TDN:
TDE:on
plugin_036_adr:60
plugin_036_rotate:1
plugin_036_nlines:1
plugin_036_scroll:1
Plugin_036_template1:espeasy
Plugin_036_template2:test
Plugin_036_template3:suite
Plugin_036_template4:test1
Plugin_036_template5:test2
Plugin_036_template6:test3
Plugin_036_template7:test4
Plugin_036_template8:test5
Plugin_036_template9:test6
Plugin_036_template10:test7
Plugin_036_template11:test8
Plugin_036_template12:test9
taskdevicepin3:-1
plugin_036_timer:0
TDT:1
edit:1
page:1
""")
if __name__=='__main__':
completed()
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# - DHT22 on D3
# tests:
# - correct values are send to domoticz via mqtt
@step()
def prepare():
node[0].reboot()
node[0].pingserial()
node[0].serialcmd("resetFlashWriteCounter")
espeasy[0].controller_domoticz_mqtt()
espeasy[0].post_device(index=1,data="""
TDNUM:5
TDN:
TDE:on
taskdevicepin1:0
plugin_005_dhttype:22
TDSD1:on
TDID1:4001
TDT:5
TDVN1:Temperature
TDF1:
TDVD1:2
TDVN2:Humidity
TDF2:
TDVD2:2
edit:1
page:1
""")
@step()
def test():
results=controller.recv_domoticz_mqtt(SENSOR_TYPE_TEMP_HUM,4001)
test_in_range(results[0], -5,40)
test_in_range(results[1], 10,80)
if __name__=='__main__':
completed()
+141
View File
@@ -0,0 +1,141 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0
# tests:
# - sending all data types for all controllers, using dummy devices
@step()
def prepare():
node[0].reboot()
node[0].pingserial()
node[0].serialcmd("resetFlashWriteCounter")
controller.clear()
#traverse most controllers
for ( title, controller_config, controller_recv ) in [
("Domoticz MQTT", espeasy[0].controller_domoticz_mqtt, controller.recv_domoticz_mqtt ),
("Domoticz HTTP", espeasy[0].controller_domoticz_http, controller.recv_domoticz_http ),
]:
@step(title)
def config_controller():
controller_config()
@step(title)
def single():
node[0].serialcmd("TaskValueSet 1,1,5001")
espeasy[0].device_p033(index=1, TDID1=5100, plugin_033_sensortype=SENSOR_TYPE_SINGLE)
results=controller_recv(SENSOR_TYPE_SINGLE,5100)
test_is(results, [ 5001 ])
@step(title)
def long():
a=50025003
#long is actually quite hackish in espeasy: since uservar only supports floats, we use 2 uservars and some bitshifting. the controller will see just one value
node[0].serialcmd("TaskValueSet 1,1,{0}".format(a & 0xffff))
node[0].serialcmd("TaskValueSet 1,2,{0}".format(a>>16))
espeasy[0].device_p033(index=1, TDID1=5101, plugin_033_sensortype=SENSOR_TYPE_LONG)
results=controller_recv(SENSOR_TYPE_LONG,5101)
test_is(results, [ a ])
@step(title)
def dual():
node[0].serialcmd("TaskValueSet 1,1,5004")
node[0].serialcmd("TaskValueSet 1,2,5005")
espeasy[0].device_p033(index=1, TDID1=5102, plugin_033_sensortype=SENSOR_TYPE_DUAL)
results=controller_recv(SENSOR_TYPE_DUAL,5102)
test_is(results, [ 5004, 5005 ])
@step(title)
def temp_hum():
node[0].serialcmd("TaskValueSet 1,1,5006")
node[0].serialcmd("TaskValueSet 1,2,5007")
espeasy[0].device_p033(index=1, TDID1=5103, plugin_033_sensortype=SENSOR_TYPE_TEMP_HUM)
results=controller_recv(SENSOR_TYPE_TEMP_HUM,5103)
test_is(results, [ 5006, 5007 ])
@step(title)
def temp_baro():
node[0].serialcmd("TaskValueSet 1,1,5008")
node[0].serialcmd("TaskValueSet 1,2,5009")
espeasy[0].device_p033(index=1, TDID1=5104, plugin_033_sensortype=SENSOR_TYPE_TEMP_BARO)
results=controller_recv(SENSOR_TYPE_TEMP_BARO,5104)
test_is(results, [ 5008, 5009 ])
@step(title)
def triple():
node[0].serialcmd("TaskValueSet 1,1,5010")
node[0].serialcmd("TaskValueSet 1,2,5011")
node[0].serialcmd("TaskValueSet 1,3,5012")
espeasy[0].device_p033(index=1, TDID1=5105, plugin_033_sensortype=SENSOR_TYPE_TRIPLE)
results=controller_recv(SENSOR_TYPE_TRIPLE,5105)
test_is(results, [ 5010, 5011, 5012 ])
@step(title)
def temp_hum_baro():
node[0].serialcmd("TaskValueSet 1,1,5013")
node[0].serialcmd("TaskValueSet 1,2,5014")
node[0].serialcmd("TaskValueSet 1,3,5015")
espeasy[0].device_p033(index=1, TDID1=5106, plugin_033_sensortype=SENSOR_TYPE_TEMP_HUM_BARO)
results=controller_recv(SENSOR_TYPE_TEMP_HUM_BARO,5106)
test_is(results, [ 5013, 5014, 5015 ])
@step(title)
def quad():
node[0].serialcmd("TaskValueSet 1,1,5016")
node[0].serialcmd("TaskValueSet 1,2,5017")
node[0].serialcmd("TaskValueSet 1,3,5018")
node[0].serialcmd("TaskValueSet 1,4,5019")
espeasy[0].device_p033(index=1, TDID1=5107, plugin_033_sensortype=SENSOR_TYPE_QUAD)
results=controller_recv(SENSOR_TYPE_QUAD,5107)
test_is(results, [ 5016, 5017, 5018, 5019 ])
@step(title)
def switch():
node[0].serialcmd("TaskValueSet 1,1,0")
espeasy[0].device_p033(index=1, TDID1=5108, plugin_033_sensortype=SENSOR_TYPE_SWITCH)
results=controller_recv(SENSOR_TYPE_SWITCH,5108)
test_is(results, [ 0 ])
node[0].serialcmd("TaskValueSet 1,1,1")
results=controller_recv(SENSOR_TYPE_SWITCH,5108)
test_is(results, [ 1 ])
@step(title)
def dimmer():
node[0].serialcmd("TaskValueSet 1,1,0")
espeasy[0].device_p033(index=1, TDID1=5109, plugin_033_sensortype=SENSOR_TYPE_DIMMER)
results=controller_recv(SENSOR_TYPE_DIMMER,5109)
test_is(results, [ 0 ])
node[0].serialcmd("TaskValueSet 1,1,25")
results=controller_recv(SENSOR_TYPE_DIMMER,5109)
test_is(results, [ 25 ])
@step(title)
def wind():
node[0].serialcmd("TaskValueSet 1,1,5022")
node[0].serialcmd("TaskValueSet 1,2,5023")
node[0].serialcmd("TaskValueSet 1,3,5024")
espeasy[0].device_p033(index=1, TDID1=5110, plugin_033_sensortype=SENSOR_TYPE_WIND)
results=controller_recv(SENSOR_TYPE_WIND,5110)
test_is(results, [ 5022, 5023, 5024 ])
if __name__=='__main__':
completed()
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
from esptest import *
# hardware requirements:
# - node 0 (will be output pin)
# - node 1 (will be input/sender)
# - D6 connected to eachother
# tests:
# - GPIO boot state
pin_input=12;
@step()
def prepare():
node[0].reboot()
node[0].pingserial()
node[0].serialcmd("resetFlashWriteCounter")
node[0].serialcmd("TaskClearAll")
node[0].serialcmd("Save")
node[1].reboot()
node[1].pingserial()
node[1].serialcmd("resetFlashWriteCounter")
node[1].serialcmd("TaskClearAll")
espeasy[1].controller_domoticz_mqtt()
espeasy[1].post_device(1, """
TDNUM:1
TDN:
TDE:on
taskdevicepin1:{pin_input}
plugin_001_type:0
plugin_001_button:0
TDSD1:on
TDID1:6000
TDT:1
TDVN1:Switch
edit:1
page:1
""".format(pin_input=pin_input))
@step()
def test():
# log.info("Setting bootsate to default and rebooting")
# node[0].http_post(
# page="hardware",
# data="""
# pled:-1
# pledi:on
# psda:4
# pscl:5
# sd:-1
# p0:0
# p2:0
# p4:0
# p5:0
# p9:0
# p10:0
# p12:0
# p13:0
# p14:0
# p15:0
# p16:0
# """)
# node[0].reboot()
# node[0].pingserial()
log.info("Setting bootsate to LOW rebooting")
espeasy[0]._node.http_post(
page="hardware",
data="""
pled:-1
pledi:on
psda:4
pscl:5
sd:-1
p0:0
p2:0
p4:0
p5:0
p9:0
p10:0
p12:1
p13:0
p14:0
p15:0
p16:0
""")
node[0].reboot()
node[0].pingserial()
controller.clear()
values=controller.recv_domoticz_mqtt(SENSOR_TYPE_SWITCH,6000)
test_is(values[0],0)
log.info("Setting bootsate to HIGH rebooting")
espeasy[0]._node.http_post(
page="hardware",
data="""
pled:-1
pledi:on
psda:4
pscl:5
sd:-1
p0:0
p2:0
p4:0
p5:0
p9:0
p10:0
p12:2
p13:0
p14:0
p15:0
p16:0
""")
node[0].reboot()
node[0].pingserial()
controller.clear()
values=controller.recv_domoticz_mqtt(SENSOR_TYPE_SWITCH,6000)
test_is(values[0],1)
if __name__=='__main__':
completed()
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env python3
import glob
import importlib
import os
from esptest import *
modules=glob.glob("test[0-9]*.py")
modules.sort()
for module in modules:
print()
print()
log.debug("Starting module "+module)
# importlib.import_module(module)
__import__(module.replace(".py",""))
log.debug("Completed module "+module)
completed()