Weather Usermod: implement blaz recommendations
This commit is contained in:
@@ -4,12 +4,6 @@
|
|||||||
|
|
||||||
#define FX_MODE_2DWEATHER 185 // can we do this here? Can we also increase modeCount here?
|
#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???
|
//utility function, move somewhere else???
|
||||||
void epochToString(time_t time, char *timeString) {
|
void epochToString(time_t time, char *timeString) {
|
||||||
tmElements_t tm;
|
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);
|
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
|
//effect function
|
||||||
uint16_t mode_2DWeather(void) {
|
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);
|
SEGMENT.fadeToBlackBy(10);
|
||||||
|
|
||||||
@@ -95,37 +86,18 @@ static const char _data_FX_MODE_2DWEATHER[] PROGMEM = "Weather@;!;!;pal=54,2d";
|
|||||||
class WeatherUsermod : public Usermod {
|
class WeatherUsermod : public Usermod {
|
||||||
private:
|
private:
|
||||||
unsigned long lastTime = 0; //will be used to download new forecast every hour
|
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
|
static const char _name[]; //usermod name
|
||||||
|
|
||||||
//config variables
|
//config variables
|
||||||
String apiKey = "get one from OpenWeatherMap.org";
|
String apiKey = "";
|
||||||
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
|
|
||||||
|
|
||||||
|
const char *cityName;
|
||||||
|
const char *countryName;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void setup() {
|
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() {
|
void connected() {
|
||||||
@@ -136,9 +108,16 @@ class WeatherUsermod : public Usermod {
|
|||||||
if (pushLoop > millis() - 1000 && (lastTime == 0 || millis() - lastTime > 3600 * 1000)) {
|
if (pushLoop > millis() - 1000 && (lastTime == 0 || millis() - lastTime > 3600 * 1000)) {
|
||||||
lastTime = millis();
|
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/
|
//https://arduinojson.org/v6/example/http-client/
|
||||||
//is this the most compact way to do http get and put it in arduinojson object???
|
//is this the most compact way to do http get and put it in arduinojson object???
|
||||||
WiFiClient client;
|
|
||||||
|
|
||||||
client.setTimeout(10000);
|
client.setTimeout(10000);
|
||||||
if (!client.connect("api.openweathermap.org", 80)) {
|
if (!client.connect("api.openweathermap.org", 80)) {
|
||||||
@@ -146,11 +125,6 @@ class WeatherUsermod : public Usermod {
|
|||||||
return;
|
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
|
// Send HTTP request
|
||||||
client.println(url);
|
client.println(url);
|
||||||
client.println(F("Host: api.openweathermap.org"));
|
client.println(F("Host: api.openweathermap.org"));
|
||||||
@@ -170,7 +144,6 @@ class WeatherUsermod : public Usermod {
|
|||||||
client.stop();
|
client.stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Serial.println(status);
|
|
||||||
|
|
||||||
// Skip HTTP headers
|
// Skip HTTP headers
|
||||||
char endOfHeaders[] = "\r\n\r\n";
|
char endOfHeaders[] = "\r\n\r\n";
|
||||||
@@ -200,70 +173,63 @@ class WeatherUsermod : public Usermod {
|
|||||||
|
|
||||||
JsonObject weatherDocObject = weatherDoc.as<JsonObject>();
|
JsonObject weatherDocObject = weatherDoc.as<JsonObject>();
|
||||||
JsonArray list = weatherDocObject[F("list")];
|
JsonArray list = weatherDocObject[F("list")];
|
||||||
|
JsonObject city = weatherDocObject["city"];
|
||||||
|
cityName = city["name"];
|
||||||
|
countryName = city["country"];
|
||||||
|
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
for (JsonObject listElement: list) {
|
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"];
|
times[i%100] = listElement["dt"];
|
||||||
|
|
||||||
JsonObject city = weatherDocObject["city"];
|
JsonObject main = listElement["main"];
|
||||||
Serial.print(" city ");
|
temps[i%100] = main["temp"];
|
||||||
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);
|
|
||||||
|
|
||||||
Serial.print(" localtime ");
|
#ifdef WLED_DEBUG
|
||||||
char localTimeString[64];
|
char timeString[64];
|
||||||
epochToString(localTime, localTimeString);
|
epochToString(listElement["dt"], timeString);
|
||||||
Serial.print(localTimeString);
|
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++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pushLoop = 0;
|
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.
|
* 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.
|
* 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
|
* Below it is shown how this could be used for e.g. a light sensor
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
void addToJsonInfo(JsonObject& root)
|
void addToJsonInfo(JsonObject& root)
|
||||||
{
|
{
|
||||||
int reading = 20;
|
|
||||||
//this code adds "u":{"Light":[20," lux"]} to the info object
|
|
||||||
JsonObject user = root["u"];
|
JsonObject user = root["u"];
|
||||||
if (user.isNull()) user = root.createNestedObject("u");
|
if (user.isNull()) user = root.createNestedObject("u");
|
||||||
|
|
||||||
JsonArray lightArr = user.createNestedArray("Light"); //name
|
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
|
||||||
lightArr.add(reading); //value
|
infoArr.add(cityName); //value
|
||||||
lightArr.add(" lux"); //unit
|
infoArr.add(countryName); //unit
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -290,10 +256,10 @@ class WeatherUsermod : public Usermod {
|
|||||||
void addToConfig(JsonObject& root)
|
void addToConfig(JsonObject& root)
|
||||||
{
|
{
|
||||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||||
top["apiKey"] = apiKey;
|
top[F("apiKey")] = apiKey;
|
||||||
top["units"] = units;
|
top[F("units")] = units;
|
||||||
top["minTemp"] = minTemp;
|
top[F("minTemp")] = minTemp;
|
||||||
top["maxTemp"] = maxTemp;
|
top[F("maxTemp")] = maxTemp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -303,10 +269,10 @@ class WeatherUsermod : public Usermod {
|
|||||||
|
|
||||||
bool configComplete = !top.isNull();
|
bool configComplete = !top.isNull();
|
||||||
|
|
||||||
configComplete &= getJsonValue(top["apiKey"], apiKey);
|
configComplete &= getJsonValue(top[F("apiKey")], apiKey);
|
||||||
configComplete &= getJsonValue(top["units"], units);
|
configComplete &= getJsonValue(top[F("units")], units);
|
||||||
configComplete &= getJsonValue(top["minTemp"], minTemp);
|
configComplete &= getJsonValue(top[F("minTemp")], minTemp);
|
||||||
configComplete &= getJsonValue(top["maxTemp"], maxTemp);
|
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 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;
|
return configComplete;
|
||||||
|
|||||||
@@ -378,7 +378,7 @@
|
|||||||
// #define FX_MODE_2DAKEMI 186 // audio enhanced
|
// #define FX_MODE_2DAKEMI 186 // audio enhanced
|
||||||
//#define FX_MODE_CUSTOMEFFECT 187 //WLEDSR Custom Effects
|
//#define FX_MODE_CUSTOMEFFECT 187 //WLEDSR Custom Effects
|
||||||
|
|
||||||
#define MODE_COUNT 186
|
#define MODE_COUNT 185
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum mapping1D2D {
|
typedef enum mapping1D2D {
|
||||||
|
|||||||
Reference in New Issue
Block a user