Weather Usermod: implement blaz recommendations

This commit is contained in:
Ewowi
2022-08-10 18:09:25 +02:00
parent 4d2cb2016e
commit c51e03fbf9
2 changed files with 63 additions and 97 deletions

View File

@@ -4,12 +4,6 @@
#define FX_MODE_2DWEATHER 185 // can we do this here? Can we also increase modeCount here?
#define pushLoopIndex 0
#define tempsIndex 1
#define timesIndex 2
#define minTempIndex 3
#define maxTempIndex 4
//utility function, move somewhere else???
void epochToString(time_t time, char *timeString) {
tmElements_t tm;
@@ -17,21 +11,18 @@ void epochToString(time_t time, char *timeString) {
sprintf(timeString, "%04d-%02d-%02d %02d:%02d:%02d", tm.Year + 1970, tm.Month, tm.Day, tm.Hour, tm.Minute, tm.Second);
}
//globals used in effect
static uint32_t pushLoop = 0; //effect pushes loop to execute. might be interesting for audioreactive too
static uint8_t units = 1; //metric (celsius) is default. (Standard=Kelvin, Imperial is Fahrenheit)
static float temps[100]; //array of temperatures
static time_t times[100]; //array of corresponding times
static float minTemp = 0; //config var
static float maxTemp = 40; //config var
//effect function
uint16_t mode_2DWeather(void) {
//or use um_data or make vars global static ...??? Looking at the amount of extra code, global static cleaner?
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_WEATHER)) {
SEGMENT.fill(SEGCOLOR(0));
return FRAMETIME;
}
uint32_t *pushLoop = (uint32_t*)um_data->u_data[pushLoopIndex]; //pointer as it is set here
float *temps = (float*)um_data->u_data[tempsIndex];
time_t *times = (time_t*)um_data->u_data[timesIndex];
float minTemp = *(float*)um_data->u_data[minTempIndex];
float maxTemp = *(float*)um_data->u_data[maxTempIndex];
*pushLoop = millis(); //will be reset to 0 in usermod loop
pushLoop = millis(); //will be reset to 0 in usermod loop
SEGMENT.fadeToBlackBy(10);
@@ -95,37 +86,18 @@ static const char _data_FX_MODE_2DWEATHER[] PROGMEM = "Weather@;!;!;pal=54,2d";
class WeatherUsermod : public Usermod {
private:
unsigned long lastTime = 0; //will be used to download new forecast every hour
uint32_t pushLoop = 0; //effect pushes loop to execute. might be interesting for audioreactive too
static const char _name[]; //usermod name
//config variables
String apiKey = "get one from OpenWeatherMap.org";
uint8_t units = 1; //metric (celsius) is default. (Standard=Kelvin, Imperial is Fahrenheit)
float minTemp = 0;
float maxTemp = 40;
float temps[100]; //array of temperatures
time_t times[100]; //array of corresponding times
String apiKey = "";
const char *cityName;
const char *countryName;
public:
void setup() {
um_data = new um_data_t;
um_data->u_size = 5;
um_data->u_type = new um_types_t[um_data->u_size];
um_data->u_data = new void*[um_data->u_size];
um_data->u_data[pushLoopIndex] = &pushLoop;
um_data->u_type[pushLoopIndex] = UMT_UINT32;
um_data->u_data[tempsIndex] = temps;
um_data->u_type[tempsIndex] = UMT_FLOAT_ARR;
um_data->u_data[timesIndex] = times;
um_data->u_type[timesIndex] = UMT_UINT32_ARR;
um_data->u_data[minTempIndex] = &minTemp;
um_data->u_type[minTempIndex] = UMT_FLOAT;
um_data->u_data[maxTempIndex] = &maxTemp;
um_data->u_type[maxTempIndex] = UMT_FLOAT;
strip.addEffect(FX_MODE_2DWEATHER, &mode_2DWeather, _data_FX_MODE_2DWEATHER);
strip.addEffect(255, &mode_2DWeather, _data_FX_MODE_2DWEATHER);
}
void connected() {
@@ -136,9 +108,16 @@ class WeatherUsermod : public Usermod {
if (pushLoop > millis() - 1000 && (lastTime == 0 || millis() - lastTime > 3600 * 1000)) {
lastTime = millis();
WiFiClient client;
char url[180];
sprintf(url, "GET /data/2.5/forecast?lat=%f&lon=%f&appid=%s&units=%s HTTP/1.0", latitude, longitude, apiKey.c_str(), units==0?"standard":units==1?"metric":"imperial");
#ifdef WLED_DEBUG
Serial.println(url);
#endif
//https://arduinojson.org/v6/example/http-client/
//is this the most compact way to do http get and put it in arduinojson object???
WiFiClient client;
client.setTimeout(10000);
if (!client.connect("api.openweathermap.org", 80)) {
@@ -146,11 +125,6 @@ class WeatherUsermod : public Usermod {
return;
}
Serial.println(F("Connected!"));
char url[180];
sprintf(url, "GET /data/2.5/forecast?lat=%f&lon=%f&appid=%s&units=%s HTTP/1.0", latitude, longitude, apiKey.c_str(), units==0?"standard":units==1?"metric":"imperial");
Serial.println(url);
// Send HTTP request
client.println(url);
client.println(F("Host: api.openweathermap.org"));
@@ -170,7 +144,6 @@ class WeatherUsermod : public Usermod {
client.stop();
return;
}
Serial.println(status);
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
@@ -200,70 +173,63 @@ class WeatherUsermod : public Usermod {
JsonObject weatherDocObject = weatherDoc.as<JsonObject>();
JsonArray list = weatherDocObject[F("list")];
JsonObject city = weatherDocObject["city"];
cityName = city["name"];
countryName = city["country"];
uint8_t i = 0;
for (JsonObject listElement: list) {
JsonObject main = listElement["main"];
char timeString[64];
epochToString(listElement["dt"], timeString);
Serial.print(timeString);
float temp = main["temp"];
Serial.print(" temp ");
Serial.print(temp);
temps[i%100] = temp;
times[i%100] = listElement["dt"];
JsonObject city = weatherDocObject["city"];
Serial.print(" city ");
Serial.print((const char *)city["name"]);
Serial.print(" - ");
Serial.print((const char *)city["country"]);
Serial.print(" sunrise ");
char sunriseString[64];
epochToString(city["sunrise"], sunriseString);
Serial.print(sunriseString);
JsonObject main = listElement["main"];
temps[i%100] = main["temp"];
Serial.print(" localtime ");
char localTimeString[64];
epochToString(localTime, localTimeString);
Serial.print(localTimeString);
#ifdef WLED_DEBUG
char timeString[64];
epochToString(listElement["dt"], timeString);
Serial.print(timeString);
Serial.print(" temp ");
Serial.print(main["temp"]);
Serial.print(" city ");
Serial.print(cityName);
Serial.print(" - ");
Serial.print(countryName);
Serial.print(" sunrise ");
char sunriseString[64];
epochToString(city["sunrise"], sunriseString);
Serial.print(sunriseString);
Serial.print(" localtime ");
char localTimeString[64];
epochToString(localTime, localTimeString);
Serial.print(localTimeString);
Serial.println();
#endif
Serial.println();
i++;
}
}
pushLoop = 0;
}
bool getUMData(um_data_t **data) //do not forget this or um_data doesn't work!! Can it be moved to superclass?
{
if (!data) return false; // no pointer provided by caller or not enabled -> exit
*data = um_data;
return true;
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
/*
void addToJsonInfo(JsonObject& root)
{
int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray lightArr = user.createNestedArray("Light"); //name
lightArr.add(reading); //value
lightArr.add(" lux"); //unit
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
infoArr.add(cityName); //value
infoArr.add(countryName); //unit
}
*/
/*
@@ -290,10 +256,10 @@ class WeatherUsermod : public Usermod {
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(FPSTR(_name));
top["apiKey"] = apiKey;
top["units"] = units;
top["minTemp"] = minTemp;
top["maxTemp"] = maxTemp;
top[F("apiKey")] = apiKey;
top[F("units")] = units;
top[F("minTemp")] = minTemp;
top[F("maxTemp")] = maxTemp;
}
@@ -303,10 +269,10 @@ class WeatherUsermod : public Usermod {
bool configComplete = !top.isNull();
configComplete &= getJsonValue(top["apiKey"], apiKey);
configComplete &= getJsonValue(top["units"], units);
configComplete &= getJsonValue(top["minTemp"], minTemp);
configComplete &= getJsonValue(top["maxTemp"], maxTemp);
configComplete &= getJsonValue(top[F("apiKey")], apiKey);
configComplete &= getJsonValue(top[F("units")], units);
configComplete &= getJsonValue(top[F("minTemp")], minTemp);
configComplete &= getJsonValue(top[F("maxTemp")], maxTemp);
// * Return true in case the config values returned from Usermod Settings were complete, or false if you'd like WLED to save your defaults to disk (so any missing values are editable in Usermod Settings)
return configComplete;

View File

@@ -378,7 +378,7 @@
// #define FX_MODE_2DAKEMI 186 // audio enhanced
//#define FX_MODE_CUSTOMEFFECT 187 //WLEDSR Custom Effects
#define MODE_COUNT 186
#define MODE_COUNT 185
#endif
typedef enum mapping1D2D {