Tasmota/tasmota/include/Powerwall.h

194 lines
5.5 KiB
C++
Executable File

// inspred by https://github.com/MoritzLerch/tesla-pv-display
#ifndef Powerwall_h
#define Powerwall_h
// include libraries
#ifdef ESP8266
#include "WiFiClientSecureLightBearSSL.h"
#else
#include <WiFiClientSecure.h>
#endif //ESP8266
class Powerwall {
private:
const char* powerwall_ip;
String tesla_email;
String tesla_password;
String authCookie;
public:
Powerwall();
String getAuthCookie();
String GetRequest(String url, String authCookie);
String GetRequest(String url);
String AuthCookie();
void resetAuthCookie();
};
Powerwall::Powerwall() {
powerwall_ip = POWERWALL_IP_CONFIG;
tesla_email = TESLA_EMAIL;
tesla_password = TESLA_PASSWORD;
authCookie = "";
}
String Powerwall::AuthCookie() {
return authCookie;
}
void Powerwall::resetAuthCookie() {
authCookie = "";
}
/**
* 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
* @returns authToken to be used in an authCookie
*/
String Powerwall::getAuthCookie() {
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: requesting new auth Cookie from %s"), powerwall_ip);
String apiLoginURL = "/api/login/Basic";
#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);
int retry = 0;
#define PW_RETRIES 5
while ((!httpsClient->connect(powerwall_ip, 443)) && (retry < PW_RETRIES)) {
delay(100);
Serial.print(".");
retry++;
}
if (retry >= PW_RETRIES) {
delete httpsClient;
return ("CONN-FAIL");
}
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: connected"));
String dataString = "{\"username\":\"customer\",\"email\":\"" + tesla_email + "\",\"password\":\"" + tesla_password + "\",\"force_sm_off\":false}";
String payload = String("POST ") + apiLoginURL + " HTTP/1.1\r\n" +
"Host: " + powerwall_ip + "\r\n" +
"Connection: close" + "\r\n" +
"Content-Type: application/json" + "\r\n" +
"Content-Length: " + dataString.length() + "\r\n" +
"\r\n" + dataString + "\r\n\r\n";
httpsClient->println(payload);
while (httpsClient->connected()) {
String response = httpsClient->readStringUntil('\n');
if (response == "\r") {
break;
}
}
String jsonInput = httpsClient->readStringUntil('\n');
char str_value[128];
str_value[0] = 0;
float fv;
JsonParser parser((char*)jsonInput.c_str());
JsonParserObject obj = parser.getRootObject();
uint32_t res = JsonParsePath(&obj, "token", '#', &fv, str_value, sizeof(str_value));
AddLog(LOG_LEVEL_DEBUG, PSTR("PWL: token: %s"), str_value);
authCookie = str_value;
delete httpsClient;
return authCookie;
}
/**
* This function does a GET-request on the local powerwall web server.
* This is mainly used here to do API requests.
* HTTP/1.0 is used because some responses are so big that this would encounter
* chunked transfer encoding in HTTP/1.1 (https://en.wikipedia.org/wiki/Chunked_transfer_encoding)
*
* @param url relative URL on the Powerwall
* @param authCookie optional, but recommended
* @returns content of request
*/
String Powerwall::GetRequest(String url, String 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(LOG_LEVEL_DEBUG, PSTR("PWL: doing GET-request to %s%s"), powerwall_ip, url.c_str());
int retry = 0;
while ((!httpsClient->connect(powerwall_ip, 443)) && (retry < 15)) {
delay(100);
Serial.print(".");
retry++;
}
if (retry >= 15) {
delete httpsClient;
return ("CONN-FAIL");
}
// HTTP/1.0 is used because of Chunked transfer encoding
httpsClient->print(String("GET ") + url + " HTTP/1.0" + "\r\n" +
"Host: " + powerwall_ip + "\r\n" +
"Cookie: " + "AuthCookie" + "=" + authCookie + "\r\n" +
"Connection: close\r\n\r\n");
while (httpsClient->connected()) {
String response = httpsClient->readStringUntil('\n');
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(LOG_LEVEL_DEBUG, PSTR("PWL: result %d"), result);
// in case of error 401, get new cookie
if (result == 401) {
authCookie = "";
resetAuthCookie();
}
}
}
if (response == "\r") {
break;
}
}
String result = httpsClient->readStringUntil('\n');
delete httpsClient;
return result;
}
/**
* this is getting called if there was no provided authCookie in powerwallGetRequest(String url, String authCookie)
*/
String Powerwall::GetRequest(String url) {
return (GetRequest(url, getAuthCookie()));
}
#endif