Mit ESP8266 API-Daten vom Solarmanager abrufen

Ich möchte mit dem ESP8266 und Arduino-Code die externen API-Daten meines Solarmanagers anzeigen lassen. Erstmal nur via Seriellen Monitor ausgeben, da mir der Rest klar ist.

Zugang ist hier: https://cloud.solar-manager.ch/v1/oauth/login

Mir geht es um:

  • Aktueller Verbrauch
  • Aktuelle Einspeisung
  • Aktuelle Solarleistung
  • Warmwasser
  • ...

Kann mir hier bitte bitte jemand helfen?

Login gibt 404.
Ansonsten schau Dir mal die ArduinoJson Lib (aktuell Version 7) oder hier an. Mit den Beispielen solltest Du die Daten problemlos auslesen können.

Gruß Tommy

ohne Code wird man dir schlecht helfen können, aber reine Annahme - dein Client am ESP8266 muss HTTPS können.

Login wirst vieleicht *) nicht brauchen, sondern die angegebene URI für eine Abfrage und den credentials in einem HTTP header field (zumindest wäre es so beschrieben: "Your App can use the token to authorize calls to the API by sending it as the HTTP Authorization header:")

*) warum hast du auf die alte Beschreibung verlinkt und nicht auf die aktuelle?

Problem gelöst. Danke trotzdem

Dann ist es im Interesse anderer Forenteilnehmer trotzdem notwendig, die Lösung hier vorzustellen.

Gruß Tommy

HI Tommy
Da hast du natürlich recht.
Ich habe also den ESP8266 verbunden mit einem LCD 16x4 und gebe dort neben dem Seriellen Monitor die Daten aus.
Einerseits die Daten des Solarmanagers über die online API,
Andererseits die Daten des Askoheat über das heimische Netzwerk.

Ich hoffe ihr könnt es nachvollziehen und nutzen. Oder weiter entwickeln.
Gruss
Robert

Code:

// Boardeinstellung für den ESP8266: NodeMCU 0.9 (ESP12-Module)
// Solarmanager-API ist hier beschrieben. https://external-web.solar-manager.ch/swagger
// Version 2.0 - Zeit stimmt nicht im Winter; Temp.abfrage OK
// Version 2.3 - Erzeugung pro Tag; Zeitabfrage aus WLAN - OK
// Version 2.4 - Erzeugung pro Tag auskommentiert ; Mit Batterieanzeige

#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <WiFiClientSecure.h>
#include <ESP8266HTTPClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <time.h>

// WiFi credentials
const char* ssid = "Dein W-Lan";
const char* password = "xxxxxxxxxxxxx";

// Solar Manager credentials (Base64 encoded username:password)
const char* authHeader = "Basic xxxxxYOUR_SOLARMANAGER_CODExxxxx";

// Client ID
const char* clientId = "xxxxxxSOLARMANAGER_IDxxxx";


// Initialize the LCD, specifying the I2C address (usually 0x27) and the dimensions (20x4)
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Custom character for degree Celsius
byte degreeCelsius[8] = {
  0b11000,
  0b11000,
  0b00000,
  0b00111,
  0b01000,
  0b01000,
  0b01000,
  0b00111
};
byte grad[8] = {
  0b00111,
  0b00101,
  0b00111,
  0b00000,
  0b00000,
  0b00000,
  0b00000,
  0b00000,
};

byte boiler[8] = {
  0b11111,
  0b10001,
  0b10101,
  0b10001,
  0b10001,
  0b10001,
  0b11111,
  0b11111,
};

void setup() {
  Serial.begin(115200);
  delay(100);

  // Initialize the I2C communication
  Wire.begin();

  // Initialize the LCD
  lcd.begin(20, 4);
  lcd.backlight();

  // Clear the display
  lcd.clear();

  // Create the custom degree Celsius character
  lcd.createChar(0, degreeCelsius);
  lcd.createChar(1, grad);
  lcd.createChar(2, boiler);

  Serial.println();
  Serial.print("Verbinde mit ");
  Serial.println(ssid);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Verbinde mit ");
  lcd.setCursor(0, 1);
  lcd.print(ssid);
  lcd.setCursor(0, 3);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    lcd.print(".");
  }

  Serial.println("");
  Serial.println("WiFi verbunden");
  Serial.print("IP-Adresse: ");
  Serial.println(WiFi.localIP());

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("WiFi verbunden");
  lcd.setCursor(0, 1);
  lcd.print("IP: ");
  lcd.print(WiFi.localIP());

  // Konfiguriere die Zeitzone für Deutschland (CET/CEST)
  configTime(3600, 3600, "pool.ntp.org", "time.nist.gov");  // 3600 Sekunden = 1 Stunde
}
void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    WiFiClientSecure client;
    HTTPClient http;

    client.setInsecure();  // Use this only if the server uses a self-signed certificate

    Serial.print("[HTTP] Verbinde mit Solar Manager...");
    if (client.connect("cloud.solar-manager.ch", 443)) {
      Serial.println("Verbunden!");

      // 1. Daten des Gateways abrufen
      String gatewayUrl = String("https://cloud.solar-manager.ch/v1/stream/gateway/") + clientId;
      http.begin(client, gatewayUrl);
      http.addHeader("accept", "application/json");
      http.addHeader("authorization", authHeader);

      int gatewayHttpCode = http.GET();

      if (gatewayHttpCode > 0) {
        String gatewayPayload = http.getString();
        Serial.println("[HTTP] Gateway Antwort empfangen");
        Serial.println(gatewayPayload);

        // Parse the received JSON payload for gateway data
        StaticJsonDocument<2048> gatewayDoc;
        DeserializationError gatewayError = deserializeJson(gatewayDoc, gatewayPayload);
        if (gatewayError) {
          Serial.print("Gateway JSON parse error: ");
          Serial.println(gatewayError.f_str());
          return;
        }

        // Ausgabe der Daten des Gateways
        Serial.println("Gateway Daten:");
        serializeJsonPretty(gatewayDoc, Serial);

        lcd.clear();

        // Ausgabe des aktuellen Stromverbrauchs
        if (gatewayDoc.containsKey("currentPowerConsumption")) {
          float currentPowerConsumption = gatewayDoc["currentPowerConsumption"];
          int roundedCurrentPowerConsumption = round(currentPowerConsumption);
          Serial.print("Aktueller Stromverbrauch: ");
          Serial.println(roundedCurrentPowerConsumption);
          lcd.setCursor(0, 1);
          lcd.print("Verbrauch: ");
          lcd.print(roundedCurrentPowerConsumption);
          lcd.print(" W");
        }

        // Ausgabe der aktuellen Solarleistung
        if (gatewayDoc.containsKey("currentPvGeneration")) {
          float currentPvGeneration = gatewayDoc["currentPvGeneration"];
          int roundedCurrentPvGeneration = round(currentPvGeneration);
          Serial.print("Aktuelle Solarleistung: ");
          Serial.println(roundedCurrentPvGeneration);
          lcd.setCursor(0, 0);
          lcd.print("PV-Anlage: ");
          lcd.print(roundedCurrentPvGeneration);
          lcd.print(" W");
        }

        // Ausgabe des aktuellen Netzbezugs oder Einspeisung
        if (gatewayDoc.containsKey("currentGridPower")) {
          float currentGridPower = gatewayDoc["currentGridPower"];
          int roundedCurrentGridPower = round(currentGridPower);
          if (roundedCurrentGridPower < 0) {
            Serial.print("Aktuelle Einspeisung: ");
            Serial.println(-roundedCurrentGridPower);
            lcd.setCursor(0, 2);
            lcd.print("Einspeis.: ");
            lcd.print(-roundedCurrentGridPower);
            lcd.print(" W");
          } else {
            Serial.print("Aktueller Netzbezug: ");
            Serial.println(roundedCurrentGridPower);
            lcd.setCursor(0, 2);
            lcd.print("Netzbezug: ");
            lcd.print(roundedCurrentGridPower);
            lcd.print(" W");
          }
        }

        // Ausgabe der aktuellen Batterieanzeige
        if (gatewayDoc.containsKey("soc")) {
          float soc = gatewayDoc["soc"];
          int roundedsoc = round(soc);
          Serial.print("Batterie: ");
          Serial.println(roundedsoc);
          lcd.setCursor(0, 3);
          lcd.print("Batterie : ");
          lcd.print(roundedsoc);
          lcd.print(" %");
        }
        /*
        // Zeit vom Netzwerk abrufen
        time_t now = time(nullptr);
        struct tm* timeInfo = localtime(&now);

        char fromTime[25];
        char correctedTimestamp[25];
        strftime(fromTime, sizeof(fromTime), "%Y-%m-%dT00:00:00.000Z", timeInfo);
        strftime(correctedTimestamp, sizeof(correctedTimestamp), "%Y-%m-%dT%H:%M:%S.000Z", timeInfo);

        // 2. Produktion abfragen für den spezifischen Zeitraum
        String productionUrl = "https://cloud.solar-manager.ch/v1/statistics/gateways/" + String(clientId) + "?accuracy=low&from=" + fromTime + "&to=" + correctedTimestamp;

        http.begin(client, productionUrl);
        http.addHeader("accept", "application/json");
        http.addHeader("authorization", authHeader);

        int productionHttpCode = http.GET();

        if (productionHttpCode > 0) {
          String productionPayload = http.getString();
          Serial.println("[HTTP] Production Antwort empfangen");
          Serial.println(productionPayload);

          Serial.print("fromTime: ");
          Serial.println(fromTime);
          Serial.print("correctedTimestamp: ");
          Serial.println(correctedTimestamp);

          // Parse production JSON payload
          StaticJsonDocument<2048> productionDoc;
          DeserializationError productionError = deserializeJson(productionDoc, productionPayload);
          if (productionError) {
            Serial.print("Production JSON parse error: ");
            Serial.println(productionError.f_str());
            return;
          }

          // Output production data
          if (productionDoc.containsKey("production")) {
            float production = productionDoc["production"];
            int roundedProduction = round(production);
            Serial.print("Produktion: ");
            Serial.println(roundedProduction);
            lcd.setCursor(0, 3);
            lcd.print("S.Energie: ");
            lcd.print(roundedProduction);
            lcd.print(" Wh");
          } else {
            Serial.print("[HTTP] Fehler beim Abrufen der Produktion: ");
            Serial.println(productionHttpCode);
          }

          http.end();  // Close production HTTP request
        } else {
          Serial.println("[HTTP] Timestamp nicht gefunden in der Gateway-Antwort");
        }

*/
      } else {
        Serial.print("[HTTP] Fehler beim Abrufen der Gateway-Antwort: ");
        Serial.println(gatewayHttpCode);
      }

      http.end();  // Close gateway HTTP request

      if (WiFi.status() == WL_CONNECTED) {
        WiFiClient client;
        HTTPClient http;

//Abfrage des Askoheat (Boilerheizung) über das Heimnetzwerk

        Serial.print("[HTTP] Verbinde mit EMA...");
        if (http.begin(client, "http://192.168.1.249/getema.json")) {
          int httpCode1 = http.GET();
          if (httpCode1 > 0) {
            String payload = http.getString();
            Serial.println("[HTTP] EMA Response received");
            Serial.println(payload);

            // Parse the received JSON payload for EMA data
            StaticJsonDocument<1024> emaDoc;
            DeserializationError error = deserializeJson(emaDoc, payload);
            if (error) {
              Serial.print("EMA JSON parse error: ");
              Serial.println(error.c_str());
              return;
            }

            // Display EMA temperatures
            if (emaDoc.containsKey("MODBUS_EMA_TEMPERATURE_FLOAT_SENSOR1") && emaDoc.containsKey("MODBUS_EMA_TEMPERATURE_FLOAT_SENSOR0")) {
              float sensor1Temp = emaDoc["MODBUS_EMA_TEMPERATURE_FLOAT_SENSOR1"].as<String>().toFloat();
              float sensor0Temp = emaDoc["MODBUS_EMA_TEMPERATURE_FLOAT_SENSOR0"].as<String>().toFloat();
              int roundedSensor1Temp = round(sensor1Temp);
              int roundedSensor0Temp = round(sensor0Temp);

              Serial.print("Sensor 1 Temperature: ");
              Serial.println(roundedSensor1Temp);
              Serial.print("Sensor 0 Temperature: ");
              Serial.println(roundedSensor0Temp);

              lcd.setCursor(18, 0);
              lcd.write(byte(1));
              lcd.setCursor(19, 0);
              lcd.print("C");

              lcd.setCursor(18, 1);
              lcd.print(roundedSensor1Temp);

              lcd.setCursor(18, 2);
              lcd.print(roundedSensor0Temp);
            }
          } else {
            Serial.print("[HTTP] Fehler beim Abrufen der EMA-Antwort: ");
            Serial.println(httpCode1);
          }
          http.end();  // Close EMA HTTP request
        } else {
          Serial.println("[HTTP] Verbindung zu EMA fehlgeschlagen");
        }
      }

    } else {
      Serial.println("[HTTP] Verbindung zum Solar Manager fehlgeschlagen");
    }

    delay(10000); 
  }
}

Mit welcher Spannung betreibst du das LCD16x4 ?
Wenn mit 5 Volt, kann das für den ESP8266 kritisch werden.
Der verträgt auf dem Pins nur 3,3Volt.

LCD16x4 mit 5V. Das ESP8266-Board hat einen Vin-Pin. Dieser geht direkt zm Spannungsregler und regelt Vin (hier 5V) auf die 3.3V. Kommunikation zum LCD über I2C.
Falsch? Läuft nun seit Wochen super.

Die PullUp deines I2C Buses sind mit 3,3V oder 5V verbunden? Wenn sie mit 5V verbunden sind, liegen 5V an den GPIOs des ESP8266. Das vertragen sie nicht lang.

Dann hast du bisher sehr viel Glück gehabt.
Die Spezifikation besagt, max. 3,3Volt auf den Datenpins des ESP. Nicht 5Volt wie bei dir, da die Pullup-Widerstände auf dem I2C-Board sicher auch mit 5Volt verbunden sind.

Danke für den Hinweis. Wie dann? Spannungsteiler?

Nein, keinen Spannungsteiler.
Du brauchst einen I2C-Levelshifter. Dann ist die Verbindung mit I2C ok.