mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e36a9f163e | ||
|
|
17f0f6637a | ||
|
|
bc91939757 | ||
|
|
eb743f95cf | ||
|
|
0ec770fc51 | ||
|
|
ed4cdc3d90 | ||
|
|
9da5f465d1 | ||
|
|
8e72ace3b5 | ||
|
|
5230a4fe4f | ||
|
|
b236887718 | ||
|
|
2527cbe097 | ||
|
|
cbca4c3efa | ||
|
|
5b0e9d934c | ||
|
|
19429e8507 | ||
|
|
a67f92176f | ||
|
|
3922827fad | ||
|
|
0c637da19f | ||
|
|
48f168da9d | ||
|
|
3357eb287c | ||
|
|
5da1c54b70 | ||
|
|
c5bbdb401a | ||
|
|
f69b84d24b | ||
|
|
1250e09fef | ||
|
|
fbf22ba7af | ||
|
|
5c26d50227 | ||
|
|
47be037be9 | ||
|
|
bc4f7d390c | ||
|
|
9fe64834f9 | ||
|
|
4d943c2386 | ||
|
|
4b298d6e31 | ||
|
|
c0c65364e5 | ||
|
|
51b76682cf | ||
|
|
5f89698308 | ||
|
|
400ee82686 | ||
|
|
485f97bdc2 | ||
|
|
5821377b6c | ||
|
|
e6e3c1ddee | ||
|
|
a6ea35ef9c | ||
|
|
fe9a579dc3 | ||
|
|
017aad051e | ||
|
|
8a30659015 | ||
|
|
ed7f597b3d | ||
|
|
6700030d7c | ||
|
|
f45c7650de | ||
|
|
a36ee65865 | ||
|
|
b61c638f0a | ||
|
|
09e5d6abcd | ||
|
|
2aafbb57a8 |
Vendored
+98
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
)
|
||||
|
||||
@@ -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"))
|
||||
Executable
+50
@@ -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()
|
||||
@@ -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"))
|
||||
Executable
+76
@@ -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()
|
||||
@@ -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)
|
||||
Executable
+41
@@ -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()
|
||||
Executable
+48
@@ -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()
|
||||
Executable
+141
@@ -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()
|
||||
Executable
+138
@@ -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
@@ -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()
|
||||
Reference in New Issue
Block a user