mirror of https://github.com/arendst/Tasmota.git
recode powerwall (#22589)
This commit is contained in:
parent
b80cc6a3e6
commit
9317e02f25
|
@ -1,17 +1,26 @@
|
||||||
|
|
||||||
// inspred by https://github.com/MoritzLerch/tesla-pv-display
|
// inspired by https://github.com/MoritzLerch/tesla-pv-display
|
||||||
#ifndef Powerwall_h
|
#ifndef Powerwall_h
|
||||||
#define Powerwall_h
|
#define Powerwall_h
|
||||||
|
|
||||||
// include libraries
|
|
||||||
#include "WiFiClientSecureLightBearSSL.h"
|
#define PW_RETRIES 2
|
||||||
|
|
||||||
|
#define PWL_LOGLVL LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
|
// include libraries from email client
|
||||||
|
// standard ssl does not work at all
|
||||||
|
ESP_SSLClient ssl_client;
|
||||||
|
WiFiClientImpl basic_client;
|
||||||
|
|
||||||
class Powerwall {
|
class Powerwall {
|
||||||
private:
|
private:
|
||||||
const char* powerwall_ip;
|
String powerwall_ip;
|
||||||
String tesla_email;
|
String tesla_email;
|
||||||
String tesla_password;
|
String tesla_password;
|
||||||
String authCookie;
|
String authCookie;
|
||||||
|
String cts1;
|
||||||
|
String cts2;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Powerwall();
|
Powerwall();
|
||||||
|
@ -19,57 +28,132 @@ class Powerwall {
|
||||||
String GetRequest(String url, String authCookie);
|
String GetRequest(String url, String authCookie);
|
||||||
String GetRequest(String url);
|
String GetRequest(String url);
|
||||||
String AuthCookie();
|
String AuthCookie();
|
||||||
void resetAuthCookie();
|
String Pwl_test(String);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef POWERWALL_IP_CONFIG
|
||||||
|
#define POWERWALL_IP_CONFIG "192.168.188.60"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TESLA_EMAIL
|
||||||
|
#define TESLA_EMAIL "email"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TESLA_PASSWORD
|
||||||
|
#define TESLA_PASSWORD "password"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TESLA_POWERWALL_CTS1
|
||||||
|
#define TESLA_POWERWALL_CTS1 "cts1"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TESLA_POWERWALL_CTS2
|
||||||
|
#define TESLA_POWERWALL_CTS2 "cts2"
|
||||||
|
#endif
|
||||||
|
|
||||||
Powerwall::Powerwall() {
|
Powerwall::Powerwall() {
|
||||||
powerwall_ip = POWERWALL_IP_CONFIG;
|
powerwall_ip = POWERWALL_IP_CONFIG;
|
||||||
tesla_email = TESLA_EMAIL;
|
tesla_email = TESLA_EMAIL;
|
||||||
tesla_password = TESLA_PASSWORD;
|
tesla_password = TESLA_PASSWORD;
|
||||||
authCookie = "";
|
authCookie = "";
|
||||||
|
cts1 = TESLA_POWERWALL_CTS1;
|
||||||
|
cts2 = TESLA_POWERWALL_CTS2;
|
||||||
}
|
}
|
||||||
|
|
||||||
String Powerwall::AuthCookie() {
|
String Powerwall::AuthCookie() {
|
||||||
return authCookie;
|
return authCookie;
|
||||||
}
|
}
|
||||||
void Powerwall::resetAuthCookie() {
|
|
||||||
authCookie = "";
|
String Powerwall::Pwl_test(String ip) {
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: try to open %s"), ip.c_str());
|
||||||
|
|
||||||
|
ssl_client.setInsecure();
|
||||||
|
/** Call setDebugLevel(level) to set the debug
|
||||||
|
* esp_ssl_debug_none = 0
|
||||||
|
* esp_ssl_debug_error = 1
|
||||||
|
* esp_ssl_debug_warn = 2
|
||||||
|
* esp_ssl_debug_info = 3
|
||||||
|
* esp_ssl_debug_dump = 4
|
||||||
|
*/
|
||||||
|
ssl_client.setDebugLevel(0);
|
||||||
|
|
||||||
|
// Set the receive and transmit buffers size in bytes for memory allocation (512 to 16384).
|
||||||
|
// For server that does not support SSL fragment size negotiation, leave this setting the default value
|
||||||
|
// by not set any buffer size or set the rx buffer size to maximum SSL record size (16384) and 512 for tx buffer size.
|
||||||
|
//ssl_client.setBufferSizes(1024 /* rx */, 512 /* tx */);
|
||||||
|
|
||||||
|
// Assign the basic client
|
||||||
|
// Due to the basic_client pointer is assigned, to avoid dangling pointer, basic_client should be existed
|
||||||
|
// as long as it was used by ssl_client for transportation.
|
||||||
|
ssl_client.setClient(&basic_client);
|
||||||
|
|
||||||
|
int retry = 0;
|
||||||
|
while (retry < PW_RETRIES) {
|
||||||
|
int32_t res = ssl_client.connect(ip.c_str(), 443);
|
||||||
|
if (res) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delay(100);
|
||||||
|
retry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retry >= PW_RETRIES) {
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: failed"));
|
||||||
|
} else {
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: connected"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_client.stop();
|
||||||
|
|
||||||
|
return "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void pHexdump(uint8_t *sbuff, uint32_t slen) {
|
||||||
|
char cbuff[slen*3+10];
|
||||||
|
char *cp = cbuff;
|
||||||
|
*cp++ = '>';
|
||||||
|
*cp++ = ' ';
|
||||||
|
for (uint32_t cnt = 0; cnt < slen; cnt ++) {
|
||||||
|
sprintf_P(cp, PSTR("%02x "), sbuff[cnt]);
|
||||||
|
cp += 3;
|
||||||
|
}
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: response: %s"), cbuff);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function returns a string with the authToken based on the basic login endpoint of
|
* This function returns a string with the authToken based on the basic login endpoint of
|
||||||
* the powerwall in combination with the credentials from the secrets.h
|
* the powerwall in combination with the credentials from the secrets.h
|
||||||
* @returns authToken to be used in an authCookie
|
* @returns authToken to be used in an authCookie
|
||||||
*/
|
*/
|
||||||
String Powerwall::getAuthCookie() {
|
String Powerwall::getAuthCookie() {
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: requesting new auth Cookie from %s"), powerwall_ip);
|
AddLog(PWL_LOGLVL, PSTR("PWL: requesting new auth Cookie from %s"), powerwall_ip.c_str());
|
||||||
String apiLoginURL = "/api/login/Basic";
|
String apiLoginURL = "/api/login/Basic";
|
||||||
|
|
||||||
#ifdef ESP32
|
ssl_client.setInsecure();
|
||||||
WiFiClientSecure *httpsClient = new WiFiClientSecure;
|
//ssl_client.setBufferSizes(4096 /* rx */, 512 /* tx */);
|
||||||
#else
|
ssl_client.setTimeout(3000);
|
||||||
// BearSSL::WiFiClientSecure_light *httpsClient = new BearSSL::WiFiClientSecure_light(1024,1024);
|
ssl_client.setClient(&basic_client);
|
||||||
WiFiClientSecure *httpsClient = new WiFiClientSecure;
|
ssl_client.setDebugLevel(3);
|
||||||
#endif
|
|
||||||
httpsClient->setInsecure();
|
|
||||||
httpsClient->setTimeout(10000);
|
|
||||||
|
|
||||||
int retry = 0;
|
int retry = 0;
|
||||||
|
while (retry < PW_RETRIES) {
|
||||||
#define PW_RETRIES 5
|
int32_t res = ssl_client.connect(powerwall_ip.c_str(), 443);
|
||||||
while ((!httpsClient->connect(powerwall_ip, 443)) && (retry < PW_RETRIES)) {
|
if (res) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
delay(100);
|
delay(100);
|
||||||
Serial.print(".");
|
|
||||||
retry++;
|
retry++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retry >= PW_RETRIES) {
|
if (retry >= PW_RETRIES) {
|
||||||
delete httpsClient;
|
|
||||||
return ("CONN-FAIL");
|
return ("CONN-FAIL");
|
||||||
}
|
}
|
||||||
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: connected"));
|
AddLog(PWL_LOGLVL, PSTR("PWL: connected"));
|
||||||
|
|
||||||
String dataString = "{\"username\":\"customer\",\"email\":\"" + tesla_email + "\",\"password\":\"" + tesla_password + "\",\"force_sm_off\":false}";
|
String dataString = "{\"username\":\"customer\",\"email\":\"" + tesla_email + "\",\"password\":\"" + tesla_password + "\",\"force_sm_off\":false}";
|
||||||
|
|
||||||
|
@ -80,31 +164,113 @@ String Powerwall::getAuthCookie() {
|
||||||
"Content-Length: " + dataString.length() + "\r\n" +
|
"Content-Length: " + dataString.length() + "\r\n" +
|
||||||
"\r\n" + dataString + "\r\n\r\n";
|
"\r\n" + dataString + "\r\n\r\n";
|
||||||
|
|
||||||
httpsClient->println(payload);
|
AddLog(PWL_LOGLVL, PSTR("PWL: payload: %s"),payload.c_str());
|
||||||
|
|
||||||
while (httpsClient->connected()) {
|
ssl_client.println(payload);
|
||||||
String response = httpsClient->readStringUntil('\n');
|
|
||||||
if (response == "\r") {
|
uint8_t flag = 0;
|
||||||
break;
|
|
||||||
|
uint8_t string[1200];
|
||||||
|
uint32_t dlen;
|
||||||
|
uint32_t timeout = 30;
|
||||||
|
while (ssl_client.connected()) {
|
||||||
|
if (ssl_client.available()) {
|
||||||
|
dlen = ssl_client.available();
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: available: %d"), dlen);
|
||||||
|
String response = "";
|
||||||
|
#if 1
|
||||||
|
if (!flag) {
|
||||||
|
char c = ssl_client.peek();
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: peek: %c"), c);
|
||||||
|
if (c != 'H') {
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: wrong response: %c"), c);
|
||||||
|
ssl_client.stop();
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
//basic_client.read(string, 17);
|
||||||
|
//ssl_client.read(string, 17);
|
||||||
|
const char *cp = ssl_client.peekBuffer();
|
||||||
|
//ssl_client.peekBytes(string, 17);
|
||||||
|
//ssl_client.peekConsume(17);
|
||||||
|
//string[17] = 0;
|
||||||
|
//pHexdump(string, 17);
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: 1. response: %s"), cp);
|
||||||
|
cp = strchr(cp, '{');
|
||||||
|
if (cp) {
|
||||||
|
char *cp1 = strchr(cp, '}');
|
||||||
|
if (cp1) {
|
||||||
|
*(cp1 + 1) = 0;
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: json: %s"), cp);
|
||||||
|
char str_value[256];
|
||||||
|
str_value[0] = 0;
|
||||||
|
float fv;
|
||||||
|
JsonParser parser((char*)cp);
|
||||||
|
JsonParserObject obj = parser.getRootObject();
|
||||||
|
uint32_t res = JsonParsePath(&obj, "token", '#', &fv, str_value, sizeof(str_value));
|
||||||
|
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: token: %s"), str_value);
|
||||||
|
|
||||||
|
ssl_client.stop();
|
||||||
|
return str_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flag = 1;
|
||||||
|
}
|
||||||
|
response = ssl_client.readStringUntil('\n');
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: response: %s"), response.c_str());
|
||||||
|
#else
|
||||||
|
ssl_client.read(string, dlen);
|
||||||
|
pHexdump(string, dlen);
|
||||||
|
#endif
|
||||||
|
char *cp = (char*)response.c_str();
|
||||||
|
if (!strncmp_P(cp, PSTR("HTTP"), 4)) {
|
||||||
|
char *sp = strchr(cp, ' ');
|
||||||
|
if (sp) {
|
||||||
|
sp++;
|
||||||
|
uint16_t result = strtol(sp, 0, 10);
|
||||||
|
if (result != 200) {
|
||||||
|
ssl_client.stop();
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (response == "\r") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeout--;
|
||||||
|
delay(100);
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: timeout: %d"), timeout);
|
||||||
|
if (!timeout) {
|
||||||
|
ssl_client.stop();
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String jsonInput = httpsClient->readStringUntil('\n');
|
String jsonInput;
|
||||||
|
dlen = ssl_client.available();
|
||||||
|
if (ssl_client.connected() && dlen) {
|
||||||
|
ssl_client.read(string, dlen);
|
||||||
|
string[dlen] = 0;
|
||||||
|
jsonInput = (char*)string;
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: jsonInput %s"),jsonInput.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
char str_value[128];
|
char str_value[256];
|
||||||
str_value[0] = 0;
|
str_value[0] = 0;
|
||||||
float fv;
|
float fv;
|
||||||
JsonParser parser((char*)jsonInput.c_str());
|
JsonParser parser((char*)jsonInput.c_str());
|
||||||
JsonParserObject obj = parser.getRootObject();
|
JsonParserObject obj = parser.getRootObject();
|
||||||
uint32_t res = JsonParsePath(&obj, "token", '#', &fv, str_value, sizeof(str_value));
|
uint32_t res = JsonParsePath(&obj, "token", '#', &fv, str_value, sizeof(str_value));
|
||||||
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: token: %s"), str_value);
|
AddLog(PWL_LOGLVL, PSTR("PWL: token: %s"), str_value);
|
||||||
|
|
||||||
authCookie = str_value;
|
ssl_client.stop();
|
||||||
|
|
||||||
delete httpsClient;
|
|
||||||
|
|
||||||
return authCookie;
|
return str_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,64 +283,138 @@ String Powerwall::getAuthCookie() {
|
||||||
* @param authCookie optional, but recommended
|
* @param authCookie optional, but recommended
|
||||||
* @returns content of request
|
* @returns content of request
|
||||||
*/
|
*/
|
||||||
String Powerwall::GetRequest(String url, String authCookie) {
|
String Powerwall::GetRequest(String url, String in_authCookie) {
|
||||||
#ifdef ESP32
|
|
||||||
WiFiClientSecure *httpsClient = new WiFiClientSecure;
|
|
||||||
#else
|
|
||||||
//BearSSL::WiFiClientSecure_light *httpsClient = new BearSSL::WiFiClientSecure_light(1024,1024);
|
|
||||||
WiFiClientSecure *httpsClient = new WiFiClientSecure;
|
|
||||||
#endif
|
|
||||||
httpsClient->setInsecure();
|
|
||||||
httpsClient->setTimeout(10000);
|
|
||||||
|
|
||||||
if (authCookie == "") {
|
|
||||||
getAuthCookie();
|
AddLog(PWL_LOGLVL, PSTR("PWL: cookie %s"), in_authCookie.c_str());
|
||||||
|
|
||||||
|
ssl_client.setInsecure();
|
||||||
|
ssl_client.setTimeout(5000);
|
||||||
|
ssl_client.setClient(&basic_client);
|
||||||
|
//ssl_client.setBufferSizes(4096 /* rx */, 512 /* tx */);
|
||||||
|
ssl_client.setBufferSizes(16384, 512);
|
||||||
|
|
||||||
|
|
||||||
|
if (in_authCookie == "") {
|
||||||
|
authCookie = getAuthCookie();
|
||||||
}
|
}
|
||||||
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: doing GET-request to %s%s"), powerwall_ip, url.c_str());
|
AddLog(PWL_LOGLVL, PSTR("PWL: doing GET-request to %s - %s"), powerwall_ip.c_str(), url.c_str());
|
||||||
|
|
||||||
int retry = 0;
|
int retry = 0;
|
||||||
|
|
||||||
while ((!httpsClient->connect(powerwall_ip, 443)) && (retry < 15)) {
|
while ((!ssl_client.connect(powerwall_ip.c_str(), 443)) && (retry < PW_RETRIES)) {
|
||||||
delay(100);
|
delay(100);
|
||||||
Serial.print(".");
|
//Serial.print(".");
|
||||||
retry++;
|
retry++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (retry >= 15) {
|
if (retry >= PW_RETRIES) {
|
||||||
delete httpsClient;
|
|
||||||
return ("CONN-FAIL");
|
return ("CONN-FAIL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: connected"));
|
||||||
|
|
||||||
// HTTP/1.0 is used because of Chunked transfer encoding
|
// HTTP/1.0 is used because of Chunked transfer encoding
|
||||||
httpsClient->print(String("GET ") + url + " HTTP/1.0" + "\r\n" +
|
String request = "GET " + url + " HTTP/1.0" + "\r\n" +
|
||||||
"Host: " + powerwall_ip + "\r\n" +
|
"Host: " + powerwall_ip + "\r\n" +
|
||||||
"Cookie: " + "AuthCookie" + "=" + authCookie + "\r\n" +
|
"Cookie: " + "AuthCookie" + "=" + authCookie + "\r\n" +
|
||||||
"Connection: close\r\n\r\n");
|
"Connection: close\r\n\r\n";
|
||||||
|
|
||||||
|
ssl_client.println(request);
|
||||||
|
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: request: %s"), request.c_str());
|
||||||
|
|
||||||
while (httpsClient->connected()) {
|
uint32_t timeout = 500;
|
||||||
String response = httpsClient->readStringUntil('\n');
|
int32_t chunked = 0;
|
||||||
char *cp = (char*)response.c_str();
|
while (ssl_client.connected()) {
|
||||||
if (!strncmp_P(cp, PSTR("HTTP"), 4)) {
|
if (ssl_client.available()) {
|
||||||
char *sp = strchr(cp, ' ');
|
String response = ssl_client.readStringUntil('\n');
|
||||||
if (sp) {
|
AddLog(PWL_LOGLVL, PSTR("PWL: result %s"), response.c_str());
|
||||||
sp++;
|
if (chunked == -2) {
|
||||||
uint16_t result = strtol(sp, 0, 10);
|
// process chunc size
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: result %d"), result);
|
chunked = strtol(response.c_str(), 0, 16);
|
||||||
// in case of error 401, get new cookie
|
AddLog(PWL_LOGLVL, PSTR("PWL: chunc size %d"), chunked);
|
||||||
if (result == 401) {
|
break;
|
||||||
authCookie = "";
|
}
|
||||||
resetAuthCookie();
|
char *cp = (char*)response.c_str();
|
||||||
|
if (!strncmp_P(cp, PSTR("HTTP"), 4)) {
|
||||||
|
char *sp = strchr(cp, ' ');
|
||||||
|
if (sp) {
|
||||||
|
sp++;
|
||||||
|
uint16_t result = strtol(sp, 0, 10);
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: result %d"), result);
|
||||||
|
// in case of error 401, get new cookie
|
||||||
|
if (result == 401) {
|
||||||
|
authCookie = "";
|
||||||
|
} else if (result != 200) {
|
||||||
|
ssl_client.stop();
|
||||||
|
return "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!strncmp_P(cp, PSTR("Transfer-Encoding: chunked"), 26)) {
|
||||||
|
chunked = -1;
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: chunked %d"), chunked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response == "\r") {
|
||||||
|
if (chunked) {
|
||||||
|
// skip
|
||||||
|
chunked = -2;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (response == "\r") {
|
timeout--;
|
||||||
|
delay(10);
|
||||||
|
if (!timeout) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = httpsClient->readStringUntil('\n');
|
String result = "\r";
|
||||||
delete httpsClient;
|
|
||||||
|
timeout = 100;
|
||||||
|
char *string = (char*)calloc(4096,1);
|
||||||
|
if (string) {
|
||||||
|
char *cp = string;
|
||||||
|
while (ssl_client.connected()) {
|
||||||
|
uint16_t dlen;
|
||||||
|
dlen = ssl_client.available();
|
||||||
|
if (dlen) {
|
||||||
|
ssl_client.read((uint8_t*)cp, dlen);
|
||||||
|
cp += dlen;
|
||||||
|
*cp = 0;
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
timeout--;
|
||||||
|
if (!timeout) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddLog(PWL_LOGLVL, PSTR("PWL: result %s"), string);
|
||||||
|
result = string;
|
||||||
|
free(string);
|
||||||
|
}
|
||||||
|
ssl_client.stop();
|
||||||
|
|
||||||
|
// custom replace
|
||||||
|
result.replace(cts1, "PW_CTS1");
|
||||||
|
|
||||||
|
result.replace(cts2, "PW_CTS2");
|
||||||
|
|
||||||
|
// shrink data size because it exceeds json parser maxsize
|
||||||
|
result.replace("communication_time", "ct");
|
||||||
|
result.replace("instant", "i");
|
||||||
|
result.replace("apparent", "a");
|
||||||
|
result.replace("reactive", "r");
|
||||||
|
|
||||||
|
result.replace("nominal_full_pack_energy", "f_p_e");
|
||||||
|
result.replace("nominal_energy_remaining", "n_e_r");
|
||||||
|
result.replace("backup_reserve_percent", "b_r_p");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,6 +422,30 @@ String Powerwall::GetRequest(String url, String authCookie) {
|
||||||
* this is getting called if there was no provided authCookie in powerwallGetRequest(String url, String authCookie)
|
* this is getting called if there was no provided authCookie in powerwallGetRequest(String url, String authCookie)
|
||||||
*/
|
*/
|
||||||
String Powerwall::GetRequest(String url) {
|
String Powerwall::GetRequest(String url) {
|
||||||
|
if (url[0] == '@') {
|
||||||
|
if (url[1] == 'D') {
|
||||||
|
// define vars
|
||||||
|
//AddLog(PWL_LOGLVL, PSTR("PWL: %s - %s - %s"), powerwall_ip.c_str(), tesla_email.c_str(), tesla_password.c_str());
|
||||||
|
url = url.substring(2);
|
||||||
|
uint16_t pos = strcspn(url.c_str(), ",");
|
||||||
|
powerwall_ip = url.substring(0, pos);
|
||||||
|
url = url.substring(pos + 1);
|
||||||
|
pos = strcspn(url.c_str(), ",");
|
||||||
|
tesla_email = url.substring(0, pos);
|
||||||
|
tesla_password = url.substring(pos + 1);
|
||||||
|
//AddLog(PWL_LOGLVL, PSTR("PWL: %s - %s - %s"), powerwall_ip.c_str(), tesla_email.c_str(), tesla_password.c_str());
|
||||||
|
return "";
|
||||||
|
} if (url[1] == 'C') {
|
||||||
|
url = url.substring(2);
|
||||||
|
uint16_t pos = strcspn(url.c_str(), ",");
|
||||||
|
cts1 = url.substring(0, pos);
|
||||||
|
cts2 = url.substring(pos + 1);
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
url = url.substring(1);
|
||||||
|
return Pwl_test(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
return (GetRequest(url, getAuthCookie()));
|
return (GetRequest(url, getAuthCookie()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -352,6 +352,7 @@ void alt_eeprom_readBytes(uint32_t adr, uint32_t len, uint8_t *buf) {
|
||||||
#include <TasmotaSerial.h>
|
#include <TasmotaSerial.h>
|
||||||
|
|
||||||
#ifdef TESLA_POWERWALL
|
#ifdef TESLA_POWERWALL
|
||||||
|
#include "SSLClient/ESP_SSLClient.h"
|
||||||
#include "include/powerwall.h"
|
#include "include/powerwall.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -773,7 +774,7 @@ typedef struct {
|
||||||
|
|
||||||
SCRIPT_MEM glob_script_mem;
|
SCRIPT_MEM glob_script_mem;
|
||||||
|
|
||||||
uint32_t Plugin_Query(uint16_t, uint8_t);
|
uint32_t Plugin_Query(uint16_t, uint8_t, char *);
|
||||||
|
|
||||||
void script_setaflg(uint8_t flg) {
|
void script_setaflg(uint8_t flg) {
|
||||||
glob_script_mem.tasm_cmd_activ = flg;
|
glob_script_mem.tasm_cmd_activ = flg;
|
||||||
|
@ -845,8 +846,8 @@ int32_t play_wave(char *path);
|
||||||
|
|
||||||
#if defined(USE_BINPLUGINS) && !defined(USE_SML_M)
|
#if defined(USE_BINPLUGINS) && !defined(USE_SML_M)
|
||||||
SML_TABLE *get_sml_table(void) {
|
SML_TABLE *get_sml_table(void) {
|
||||||
if (Plugin_Query(53, 0)) {
|
if (Plugin_Query(53, 0, 0)) {
|
||||||
return (SML_TABLE*)Plugin_Query(53, 1);
|
return (SML_TABLE*)Plugin_Query(53, 1, 0);
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2585,19 +2586,23 @@ uint32_t match_vars(char *dvnam, TS_FLOAT **fp, char **sp, uint32_t *ind) {
|
||||||
if (slen == olen && *cp == dvnam[0]) {
|
if (slen == olen && *cp == dvnam[0]) {
|
||||||
if (!strncmp(cp, dvnam, olen)) {
|
if (!strncmp(cp, dvnam, olen)) {
|
||||||
uint16_t index = vtp[count].index;
|
uint16_t index = vtp[count].index;
|
||||||
if (vtp[count].bits.is_string == 0) {
|
if (vtp[count].bits.global > 0) {
|
||||||
if (vtp[count].bits.is_filter) {
|
if (vtp[count].bits.is_string == 0) {
|
||||||
// error
|
if (vtp[count].bits.is_filter) {
|
||||||
return 0;
|
// error
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
*fp = &glob_script_mem.fvars[index];
|
||||||
|
*ind = count;
|
||||||
|
return NUM_RES;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*fp = &glob_script_mem.fvars[index];
|
*sp = glob_script_mem.glob_snp + (index * glob_script_mem.max_ssize);
|
||||||
*ind = count;
|
*ind = count;
|
||||||
return NUM_RES;
|
return STR_RES;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*sp = glob_script_mem.glob_snp + (index * glob_script_mem.max_ssize);
|
return 0;
|
||||||
*ind = count;
|
|
||||||
return STR_RES;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2839,15 +2844,15 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, TS_FLOAT *fp, char *
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *term="\n\r ])=+-/*%><!^&|}{";
|
const char *term="\n\r ])=+-/*%><!^&|}{";
|
||||||
for (count = 0; count < sizeof(vname); count++) {
|
for (count = 0; count < sizeof(vname) - 1; count++) {
|
||||||
char iob = lp[count];
|
char iob = lp[count];
|
||||||
if (!iob || strchr(term, iob)) {
|
if (!iob || strchr(term, iob)) {
|
||||||
vname[count] = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
vname[count] = iob;
|
vname[count] = iob;
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
|
vname[count] = 0;
|
||||||
|
|
||||||
if (!vname[0]) {
|
if (!vname[0]) {
|
||||||
// empty string
|
// empty string
|
||||||
|
@ -3515,6 +3520,10 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
|
||||||
goto nfuncexit;
|
goto nfuncexit;
|
||||||
}
|
}
|
||||||
#endif //USE_ENERGY_SENSOR
|
#endif //USE_ENERGY_SENSOR
|
||||||
|
if (!strncmp_XP(vname, XPSTR("ethdwn"), 6)) {
|
||||||
|
fvar = TasmotaGlobal.global_state.eth_down;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
//#define DEBUG_FS
|
//#define DEBUG_FS
|
||||||
|
@ -4309,9 +4318,11 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
|
||||||
|
|
||||||
#ifdef TESLA_POWERWALL
|
#ifdef TESLA_POWERWALL
|
||||||
if (!strncmp_XP(lp, XPSTR("gpwl("), 5)) {
|
if (!strncmp_XP(lp, XPSTR("gpwl("), 5)) {
|
||||||
char path[SCRIPT_MAX_SBSIZE];
|
char *path;
|
||||||
lp = GetStringArgument(lp + 5, OPER_EQU, path, 0);
|
//lp = GetStringArgument(lp + 5, OPER_EQU, path, 0);
|
||||||
|
lp = GetLongIString(lp + 5, &path);
|
||||||
fvar = call2pwl(path);
|
fvar = call2pwl(path);
|
||||||
|
free(path);
|
||||||
goto nfuncexit;
|
goto nfuncexit;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -4849,10 +4860,14 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
|
||||||
#ifdef USE_BINPLUGINS
|
#ifdef USE_BINPLUGINS
|
||||||
if (!strncmp_XP(lp, XPSTR("mo("), 3)) {
|
if (!strncmp_XP(lp, XPSTR("mo("), 3)) {
|
||||||
TS_FLOAT fvar1;
|
TS_FLOAT fvar1;
|
||||||
|
TS_FLOAT fvar2;
|
||||||
lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, gv);
|
lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, gv);
|
||||||
SCRIPT_SKIP_SPACES
|
SCRIPT_SKIP_SPACES
|
||||||
|
lp = GetNumericArgument(lp, OPER_EQU, &fvar2, gv);
|
||||||
|
SCRIPT_SKIP_SPACES
|
||||||
|
uint16_t par = ((uint8_t)fvar1) << 8 | (uint8_t)fvar2;
|
||||||
|
|
||||||
char *rbuff = (char*)Plugin_Query(126, fvar1);
|
char *rbuff = (char*)Plugin_Query(126, par, 0);
|
||||||
if (rbuff) {
|
if (rbuff) {
|
||||||
if (sp) strlcpy(sp, rbuff, glob_script_mem.max_ssize);
|
if (sp) strlcpy(sp, rbuff, glob_script_mem.max_ssize);
|
||||||
free (rbuff);
|
free (rbuff);
|
||||||
|
@ -4967,6 +4982,18 @@ extern void W8960_SetGain(uint8_t sel, uint16_t value);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
#endif // USE_I2S_AUDIO
|
#endif // USE_I2S_AUDIO
|
||||||
|
|
||||||
|
#if defined(USE_BINPLUGINS) && !defined(USE_I2S_AUDIO)
|
||||||
|
if (!strncmp_XP(lp, XPSTR("pl("), 3)) {
|
||||||
|
char path[SCRIPT_MAX_SBSIZE];
|
||||||
|
lp = GetStringArgument(lp + 3, OPER_EQU, path, 0);
|
||||||
|
Plugin_Query(42, 0, path);
|
||||||
|
len++;
|
||||||
|
len = 0;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
#endif // USE_BINPLUGINS
|
||||||
|
|
||||||
if (!strncmp_XP(lp, XPSTR("pd["), 3)) {
|
if (!strncmp_XP(lp, XPSTR("pd["), 3)) {
|
||||||
GetNumericArgument(lp + 3, OPER_EQU, &fvar, gv);
|
GetNumericArgument(lp + 3, OPER_EQU, &fvar, gv);
|
||||||
uint8_t gpiopin = fvar;
|
uint8_t gpiopin = fvar;
|
||||||
|
@ -6355,7 +6382,7 @@ void tmod_directModeOutput(uint32_t pin);
|
||||||
if (!strncmp_XP(lp, XPSTR("wso("), 4)) {
|
if (!strncmp_XP(lp, XPSTR("wso("), 4)) {
|
||||||
TS_FLOAT port;
|
TS_FLOAT port;
|
||||||
lp = GetNumericArgument(lp + 4, OPER_EQU, &port, gv);
|
lp = GetNumericArgument(lp + 4, OPER_EQU, &port, gv);
|
||||||
if (TasmotaGlobal.global_state.wifi_down) {
|
if (TasmotaGlobal.global_state.network_down) {
|
||||||
fvar = - 2;
|
fvar = - 2;
|
||||||
} else {
|
} else {
|
||||||
if (glob_script_mem.tcp_server) {
|
if (glob_script_mem.tcp_server) {
|
||||||
|
@ -7123,7 +7150,6 @@ char *GetLongIString(char *lp, char **dstr) {
|
||||||
lp = GetStringArgument(lp, OPER_EQU, *dstr, 0);
|
lp = GetStringArgument(lp, OPER_EQU, *dstr, 0);
|
||||||
} else {
|
} else {
|
||||||
lp++;
|
lp++;
|
||||||
|
|
||||||
char *cp;
|
char *cp;
|
||||||
#if 0
|
#if 0
|
||||||
cp = strchr(lp, '"');
|
cp = strchr(lp, '"');
|
||||||
|
@ -7132,7 +7158,7 @@ char *GetLongIString(char *lp, char **dstr) {
|
||||||
*dstr = (char*)calloc(slen + 2, 1);
|
*dstr = (char*)calloc(slen + 2, 1);
|
||||||
if (!*dstr) return lp;
|
if (!*dstr) return lp;
|
||||||
memmove(*dstr, lp , slen);
|
memmove(*dstr, lp , slen);
|
||||||
#else
|
#else
|
||||||
char *llp = lp;
|
char *llp = lp;
|
||||||
loop:
|
loop:
|
||||||
cp = strchr(lp, '"');
|
cp = strchr(lp, '"');
|
||||||
|
@ -13022,21 +13048,6 @@ int32_t call2pwl(const char *url) {
|
||||||
String result = powerwall.GetRequest(String(url), cookie);
|
String result = powerwall.GetRequest(String(url), cookie);
|
||||||
//AddLog(LOG_LEVEL_INFO, PSTR("PWL: result: %s"), result.c_str());
|
//AddLog(LOG_LEVEL_INFO, PSTR("PWL: result: %s"), result.c_str());
|
||||||
|
|
||||||
// shrink data size because it exceeds json parser maxsize
|
|
||||||
result.replace("communication_time", "ct");
|
|
||||||
result.replace("instant", "i");
|
|
||||||
result.replace("apparent", "a");
|
|
||||||
result.replace("reactive", "r");
|
|
||||||
|
|
||||||
// custom replace
|
|
||||||
#ifdef TESLA_POWERWALL_CTS1
|
|
||||||
result.replace(TESLA_POWERWALL_CTS1, "PW_CTS1");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TESLA_POWERWALL_CTS2
|
|
||||||
result.replace(TESLA_POWERWALL_CTS2, "PW_CTS2");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (result.length()>4095) {
|
if (result.length()>4095) {
|
||||||
AddLog(LOG_LEVEL_INFO, PSTR("PWL: result overflow: %d"), result.length());
|
AddLog(LOG_LEVEL_INFO, PSTR("PWL: result overflow: %d"), result.length());
|
||||||
}
|
}
|
||||||
|
@ -13160,9 +13171,16 @@ uint32_t script_i2c(uint8_t sel, uint16_t val, uint32_t val1) {
|
||||||
switch (sel) {
|
switch (sel) {
|
||||||
case 0:
|
case 0:
|
||||||
glob_script_mem.script_i2c_addr = val;
|
glob_script_mem.script_i2c_addr = val;
|
||||||
#if defined(ESP32) && defined(USE_I2C_BUS2)
|
#ifdef ESP32
|
||||||
if (val1 == 0) glob_script_mem.script_i2c_wire = &Wire;
|
if (val1 == 0) glob_script_mem.script_i2c_wire = &Wire;
|
||||||
else glob_script_mem.script_i2c_wire = &Wire1;
|
else {
|
||||||
|
#if defined(USE_I2C_BUS2)
|
||||||
|
glob_script_mem.script_i2c_wire = &Wire1;
|
||||||
|
#else
|
||||||
|
glob_script_mem.script_i2c_wire = &Wire;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
glob_script_mem.script_i2c_wire = &Wire;
|
glob_script_mem.script_i2c_wire = &Wire;
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue