Smarthome Wolkenlampe mit Wetteranzeige



  • Ich habe vor kurzem ein Video gesehen, in dem jemand eine Lampe gebaut hat, die aussieht wie eine Wolke. Die Idee hat mir super gefallen und da dachte ich mir, ich will das ganze auch umsetzen und nachbauen. Das war relativ einfach.

    • 2x Lampenschirme besorgt
    • vier Dosen Sprühkleber
    • eine Dose Haarspray fürs Finish
    • Füllmaterials als “Wolke”
    • ein bisschen Heißkleber und Draht zum verbinden der beiden Lampenschirme

    Wenn man das alles kombiniert, sieht’s am Ende ungefähr so aus:

    Wolken Lampe DIY - Lampenschrime miteinander verklebt

    Wolken Lampe DIY - Lampenschirme mit Füllmaterial beklebt

    Bis zu dem Stand hat das ganze ca. 25€ gekostet. Für so eine stylische Lampe eigentlich Konkurrenzlos. 🙈 Fehlen eigentlich nur noch die LED’s im Inneren.

    Ich habe mich erstmal für normale RGB-LED-Streifen entschieden.

    Wolken Lampe DIY - RGB LED

    So weit so gut, nur reicht mir das so noch nicht: Meine Lampe soll smart werden. Und damit herzlich Willkommen zu meiner kleinen Projekt-Dokumentation. 🙂


    Projekt-Dokumentation:

    Meine smarte Wolkenlampe mit Wetteranzeige


    Smart bedeutet in dem Fall nicht nur steuerbar über das Handy und per Sprache via Siri, sondern ich möchte, dass meine Wolkenlampe das Wetter und die Uhrzeit widerspiegelt.

    Bedeutet im Klartext: Die Wolkenlampe soll mit dem wetter und der Uhrzeit synchronisiert werden. Und das völlig automatisch.

    Morgens und Abends soll meine Wolke jeweils in Rot / Orange / Gelb / Lila Farbtönen erleuchten. Wie ein Sonnenauf und -untergang eben aussieht. 🙂

    Tagsüber soll die Lampe einfach nur in strahlendem weiß leuchten wenn schönes Wetter ist, und gräulich wenn es regnet.

    Als Highlight, und um die passende Atmosphäre zu erzeugen, möchte ich, dass die Wolkenlampe zum Blitzen anfängt, wenn es draußen gewittert. 🌧

    Das ist der Plan, und ungefähr so soll das Farbspiel am Ende aussehen:

    Morgens / Abends

    Wolken Lampe DIY - Morgens und Abends

    Tagsüber

    Wolken Lampe DIY - Tagsüber

    Gewitter / schlechtes Wetter

    Wolken Lampe DIY - bei Gewitter

    Realisieren möchte ich das ganze mit WS2812 LED’s und einer API eines Wetteranbieters. Das wird mein erstes WS2812 Projekt, und ich hab richtig bock. Abonniert das Thema wenn ihr wollt, ich werde meine Updates zu dem Projekt hier veröffentlichen.


    🔔 Aktiviert die Glocke für Benachrichtigungen 🔔


    Hier gehts weiter:


    To be continued…



  • Die Wetter-API

    Als API für das aktuelle Wetter habe ich mich für die kostenlose Variante der openweathermap.org entschieden. Ich denke die Anzahl der Anfragen sollte für meinen Zweck hier locker ausreichen. Nach kurzer Recherche im Internet sieht es auch vielversprechend aus was die Zuverlässigkeit und Genauigkeit betrifft.

    Hier mal die Leistungen des Free-Plans:

    • 60 Anfragen/Minute
    • 1.000.000 Anfragen/Monat

    • Current Weather
    • Minute Forecast 1 hour*
    • Hourly Forecast 2 days*
    • Daily Forecast 7 days*
    • Historical weather 5 days*

    (*) auf 1000 Anfragen am Tag beschränkt.

    Nach der kostenlosen Registrierung steht der persönliche API-Schlüssel direkt zur Verfügung und kann genutzt werden, um auf die API zuzugreifen.

    Der API-Call ist ziemlich einfach und liefert folgendes Ergebnis

    http://api.openweathermap.org/data/2.5/weather?q={STADT}&appid={API-TOKEN}

    {
    	"coord": {
    		"lon": 11.58,
    		"lat": 48.14
    	},
    	"weather": [{
    		"id": 800,
    		"main": "Clear",
    		"description": "clear sky",
    		"icon": "01d"
    	}],
    	"base": "stations",
    	"main": {
    		"temp": 300.87,
    		"feels_like": 302.24,
    		"temp_min": 300.37,
    		"temp_max": 301.48,
    		"pressure": 1022,
    		"humidity": 52
    	},
    	"visibility": 10000,
    	"wind": {
    		"speed": 1.41,
    		"deg": 339
    	},
    	"clouds": {
    		"all": 0
    	},
    	"dt": 1600173992,
    	"sys": {
    		"type": 3,
    		"id": 2002112,
    		"country": "DE",
    		"sunrise": 1600145467,
    		"sunset": 1600190802
    	},
    	"timezone": 7200,
    	"id": 2867714,
    	"name": "Munich",
    	"cod": 200
    }
    

    Man erhält erstaunlich viele Daten mit denen man arbeiten kann. Mehr als ich erwartet hatte. Mal sehen ob man davon noch was sinnvoll verwenden kann, auch für andere Projekte. Erstmal sind für mich aber folgende Objekte relevant:

    "sys": {
    	"type": 3,
    	"id": 2002112,
    	"country": "DE",
    	"sunrise": 1600145467,
    	"sunset": 1600190802
    }
    
    "weather": [{
    	"id": 800,
    	"main": "Clear",
    	"description": "clear sky",
    	"icon": "01d"
    }]
    

    Da es in München aktuell super schön ist, werde ich erstmal rausfinden müssen, was mir bei verschiedenen Wetterlagen zurückgegeben wird. 🙂


    In Hamburg ist es gerade leicht Bewölkt:

    "weather": [{
    	"id": 802,
    	"main": "Clouds",
    	"description": "scattered clouds",
    	"icon": "03n"
    }],
    
    "clouds": {
    	"all": 40
    }
    

    In Brynamman (UK) regnet es:

    "weather": [{
    	"id": 500,
    	"main": "Rain",
    	"description": "light rain",
    	"icon": "10d"
    }],
    
    "clouds": {
    	"all": 87
    }
    

    Aber bevor ihr jetzt ebenfalls anfängt, die aktuelle Wetterkarte zu studieren wo es regnet oder Gewittert, könnt ihr leichter einen Blick in die API-Dokumentation werfen.

    Hier mal die Wetter-Codes und die passenden Bezeichnungen dazu:
    https://openweathermap.org/weather-conditions

    Hier allgemein die Keys und Values und deren Bedeutung:
    https://openweathermap.org/weather-data



  • API Daten verarbeiten

    Ich habe gerade die API in einem Arduino Programm verarbeitet. Entweder läuft das ganze am Ende auf einem ESP01-S oder einem D1 Mini oder Ähnlichem. Je nachdem was die Leistung hergibt.


    Der Code holt sich das JSON-Objekt von der API und verarbeitet es dementsprechend weiter. Auf den ersten Blick sieht das Programm ziemlich wild aus, ist aber eigentlich nichts spektakuläres:

    #include <ESP8266WiFi.h>
    #include <ESP8266HTTPClient.h>
    
    #include <ArduinoJson.h>
    
    
    HTTPClient sender;
    
    
    // #####################################################################
    // USER DATA USER DATA USER DATA USER DATA USER DATA USER DATA USER DATA 
    
    
    // #####################################################################
    // WLAN-Daten
    const char* ssid = "WLAN-SSID";
    const char* password = "WLAN-PSK";
    
    // #####################################################################
    // home.openweathermap.org/api_keys
    const int zipcode = 12345;
    const String apikey = "d96f32a2a42s3832fa0695fx4553c5c4";
    
    
    // USER DATA USER DATA USER DATA USER DATA USER DATA USER DATA USER DATA
    // ##################################################################### 
    
    
    
    String json;
    
    String city_name;
    String weather_description;
    int weather_id;
    
    
    /*
     * 
     * DEFINITIONEN DER EINZELENEN WETTER-IDs
     * 
     */
    
    // THUNDERSTORM // GEWITTER
    int thunderstorm[]={200, 201, 2020, 210, 211, 212, 221, 230, 231, 232};
    
    // DRIZZLE // NIESELREGEN
    int drizzle[]={300, 301, 302, 310, 311, 312, 313, 314, 321};
    
    // RAIN // REGEN
    int rain[]={500, 501, 502, 503, 504, 511, 520, 521, 522, 531};
    
    // SNOW // SCHNEE
    int snow[]={600, 601, 602, 611, 612, 613, 615, 616, 620, 621, 622};
    
    // ATMOSPHERE // ATMOSPHÄRE
    int atmosphere[]={701, 711, 721, 731, 741, 751, 761, 762, 771, 781};
    
    // CLEAR // KLAR
    int clear_0[]={800};
    
    // CLOUDS // BEWÖLKT
    int clouds[]={801, 802, 803, 804};
    
    
    void setup() {
      Serial.begin(115200);
      
      WiFi.begin(ssid, password);
    
      while (WiFi.status() != WL_CONNECTED) {
        delay(200);
        Serial.print(".");
      }
    
      Serial.println("Verbunden!");
    
      
    
      // JSON-Objekt von der API abfragen und in Variable speichern
      json = get_weather_json(zipcode, apikey);
    
      // JSON Objekt ausgeben (DEBUG)
      Serial.println(json);
    
      
      // Values der Keys in die einzelnen Variablen speichern
      city_name = get_city_name(json);
      weather_description = get_weather_description(json);
      weather_id = get_weather_id(json);
    
      // Ausgabe der einzelnen Variablen (DEBUG)
      Serial.println(city_name + " :: " + weather_description + " :: " + weather_id);
    
    
    
    
      //HIER NUR EIN BEISPIEL ZUM VERABRITEN
    
      if (std::find(std::begin(thunderstorm), std::end(thunderstorm), weather_id) != std::end(thunderstorm)){
        Serial.println("Es ist herrscht ein Gewitter!");
      }
    
      if (std::find(std::begin(drizzle), std::end(drizzle), weather_id) != std::end(drizzle)){
        Serial.println("Es ist herrscht Nieselregen!");
      }
    
      if (std::find(std::begin(rain), std::end(rain), weather_id) != std::end(rain)){
        Serial.println("Es ist regnet!");
      }
    
      if (std::find(std::begin(snow), std::end(snow), weather_id) != std::end(snow)){
        Serial.println("Es ist schneit!");
      }
    
      if (std::find(std::begin(atmosphere), std::end(atmosphere), weather_id) != std::end(atmosphere)){
        Serial.println("Trübe Atmosphäre!");
      }
    
      if (std::find(std::begin(clear_0), std::end(clear_0), weather_id) != std::end(clear_0)){
        Serial.println("Der Himmel ist klar!");
      }
    
      if (std::find(std::begin(clouds), std::end(clouds), weather_id) != std::end(clouds)){
        Serial.println("Es ist (leicht) bewölkt!");
      }
    
    
      //HIER NUR EIN BEISPIEL ZUM VERABRITEN
    
     
      
    }
    
    
    void loop() {
    
      
    
    }
    
    
    /*
     * 
     * 
     * ##### DEFINED FUNCTIONS #####
     * 
     * 
     */
    
    
    String get_weather_json(int zipcode, String apikey){
    
      if (sender.begin("http://api.openweathermap.org/data/2.5/weather?zip=" + String(zipcode) + ",DE&appid=" + apikey)) {
    
        // HTTP-Code der Response speichern
        int httpCode = sender.GET();
       
    
        if (httpCode > 0) {
          
          // Anfrage wurde gesendet und Server hat geantwortet
          // Info: Der HTTP-Code für 'OK' ist 200
          if (httpCode == HTTP_CODE_OK) {
    
            // Hier wurden die Daten vom Server empfangen
    
            // String vom Webseiteninhalt speichern
            String payload = sender.getString();
    
            // Hier kann mit dem Wert weitergearbeitet werden
            // ist aber nicht unbedingt notwendig
            // Serial.println(payload);
    
            
            return payload;
            
          }
          
        }else{
          // Falls HTTP-Error
          Serial.printf("HTTP-Error: ", sender.errorToString(httpCode).c_str());
        }
    
        // Wenn alles abgeschlossen ist, wird die Verbindung wieder beendet
        sender.end();
        
      }else {
        Serial.printf("HTTP-Verbindung konnte nicht hergestellt werden!");
      }
      
    }
    
    
    String get_city_name(String input_json){
    
      const size_t capacity = 800;
      DynamicJsonDocument doc(capacity);
            
      const char* json = input_json.c_str();
    
      deserializeJson(doc, json);
    
      const char* cityname = doc["name"];
    
      return String(cityname);
      
    }
    
    String get_weather_description(String input_json){
    
      const size_t capacity = 800;
      DynamicJsonDocument doc(capacity);
            
      const char* json = input_json.c_str();
    
      deserializeJson(doc, json);
      JsonObject weather_0 = doc["weather"][0];
    
      const char* weather_0_description = weather_0["description"];
    
      return String(weather_0_description);
      
    }
    
    int get_weather_id(String input_json){
    
      const size_t capacity = 800;
      DynamicJsonDocument doc(capacity);
            
      const char* json = input_json.c_str();
    
      deserializeJson(doc, json);
      JsonObject weather_0 = doc["weather"][0];
    
      int weather_0_id = weather_0["id"];
    
      return weather_0_id;
      
    }
    

    Mit dem folgendem Code-Snippet wird die API abgerufen. Die Einzelnen Werte werden dementsprechend in Variablen gespeichert. Für mich sind nur 3 Values ausschlaggebend.

    Der Ort, die Wetter-Beschreibung und die Wetter-ID.

    Für den eigentlichen Call und die Ausgabe sind nur die folgenden Zeilen verantwortlich:

      // JSON-Objekt von der API abfragen und in Variable speichern
      json = get_weather_json(zipcode, apikey);
    
      // JSON Objekt ausgeben (DEBUG)
      Serial.println(json);
    
      
      // Values der Keys in die einzelnen Variablen speichern
      city_name = get_city_name(json);
      weather_description = get_weather_description(json);
      weather_id = get_weather_id(json);
    
      // Ausgabe der einzelnen Variablen (DEBUG)
      Serial.println(city_name + " :: " + weather_description + " :: " + weather_id);
    
    München :: few clouds :: 801
    

    Der Rest des Codes sind die definierten Funktionen auf die zugegriffen werden. Also u.a get_weather_id(), get_weather_json() oder get_city_name().

    Ihr könnt das Programm 1:1 kopieren und einfügen und natürlich testen. Ihr müsst nur 4 Parameter anpassen:

    // WLAN-Zugangsdaten
    const char* ssid = "WLAN-SSID";
    const char* password = "WLAN-PSK";
    
    // PLZ und API-Key
    const int zipcode = 12345;
    const String apikey = "d96f32a2a42s3832fa0695fx4553c5c4";
    

    Ich halte euch auf dem Laufenden. 👍