diff --git a/include/NTP.h b/include/NTP.h index 61a8c754..5911bef4 100644 --- a/include/NTP.h +++ b/include/NTP.h @@ -15,5 +15,6 @@ extern const String getDateTimeDotFormated(); extern const String getTodayDateDotFormated(); extern unsigned long strDateToUnix(String date); extern const String getDateTimeDotFormatedFromUnix(unsigned long unixTime); +extern const String getTimeDotFormatedFromUnix(unsigned long unixTime); extern unsigned long gmtTimeToLocal(unsigned long gmtTimestamp); extern const String getDateDotFormatedFromUnix(unsigned long unixTime); diff --git a/src/NTP.cpp b/src/NTP.cpp index 046f1612..6d4a44e1 100644 --- a/src/NTP.cpp +++ b/src/NTP.cpp @@ -184,6 +184,14 @@ const String getDateTimeDotFormatedFromUnix(unsigned long unixTime) { return String(buf); } +const String getTimeDotFormatedFromUnix(unsigned long unixTime) { + Time_t time; + breakEpochToTime(unixTime, time); + char buf[32]; + sprintf(buf, "%02d:%02d:%02d", time.hour, time.minute, time.second); + return String(buf); +} + const String getDateDotFormatedFromUnix(unsigned long unixTime) { Time_t time; breakEpochToTime(unixTime, time); diff --git a/src/modules/virtual/owmWeather/modinfo.json b/src/modules/virtual/owmWeather/modinfo.json new file mode 100644 index 00000000..e425d9fc --- /dev/null +++ b/src/modules/virtual/owmWeather/modinfo.json @@ -0,0 +1,82 @@ +{ + "menuSection": "virtual_elments", + + "configItem": [ + { + "global": 0, + "name": "Погода OWM", + "type": "Reading", + "subtype": "owmWeather", + "id": "owm", + "needSave": 0, + "widget": "nil", + "page": "Погода", + "descr": "Температура на улице", + "int": 30, + "API_key": "", + "сity": "Moscow", + "lon": "37.54", + "lat": "57.74", + "lang": "ru", + "param": "", + "round": 1, + "val": "...", + "debug": 0 + } + ], + + "about": { + "authorName": "Serghei Crasnicov, v2.0 Mikhail Bubnov", + "authorContact": "https://t.me/Serghei63", + "authorGit": "https://github.com/Serghei63", + "specialThanks": "AVAKS", + "moduleName": "owmWeather", + "moduleVersion": "2.0", + "usedRam": { + "esp32_4mb": 15, + "esp8266_4mb": 15 + }, + "subTypes": [ + "temp", + "humidity", + "pressure", + "speed", + "deg", + "all", + "main", + "description", + "icon", + "sunrise", + "sunset", + "name" + ], + "title": "Погода", + "moduleDesc": "Получение погоды из интернет OpenWeatherMap", + "propInfo": { + "param": "Тип текущего Item: temp - температура, humidity - влажность, pressure - давление, speed - скорость ветра, deg - направление ветра, all - процент облачности, sunrise - рассвет, sunset - закат, description - Погодные условия, icon - код иконки, name - город. Если оставить пустым пудет искать и публиковать при изменении в Items с именами wea_temp и т.д. wea_...", + "int": "Интервал запроса погоды в минутах", + "API_key": "API ключ", + "сity": "Название города, через запятую можно уочнить код страны. Наример Moscow или Moscow,ru или Москва. Если город не задан будут использоваться координаты. OWM рекомендует координаты", + "lon": "Долгота, при использовании координат, будет автоматически выбран ближайший город", + "lat": "Широта, при использовании координат, будет автоматически выбран ближайший город", + "lang": "Язык используемый в ответах OpenWetaherMap", + "debug":"1 - выводить дополнительный лог в сериал" + } + }, + + "defActive": true, + + "usedLibs": { + "esp32_4mb": [], + "esp32s2_4mb": [], + "esp32_16mb": [], + "esp8266_4mb": [], + "esp8266_16mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [], + "esp8266_2mb": [], + "esp8266_2mb_ota": [] + } +} diff --git a/src/modules/virtual/owmWeather/owmWeather.cpp b/src/modules/virtual/owmWeather/owmWeather.cpp new file mode 100644 index 00000000..400be181 --- /dev/null +++ b/src/modules/virtual/owmWeather/owmWeather.cpp @@ -0,0 +1,233 @@ +#include "Global.h" +#include "classes/IoTItem.h" +#include +#include "NTP.h" +// long prevWeatherMillis = millis() - 60001; +// TODO Зачем так много??? +StaticJsonDocument Weatherdoc1; + +extern IoTGpio IoTgpio; +class owmWeather : public IoTItem +{ +private: + // описание параметров передаваемых из настроек датчика из веба + + String _location; + String _param; + // long interval; + String _API_key; + String _сity = ""; + String _lat = ""; + String _lon = ""; + String _lang = ""; + bool _debug = false; + +public: + owmWeather(String parameters) : IoTItem(parameters) + { + _API_key = jsonReadStr(parameters, "API_key"); + // _ID_sity = jsonReadStr(parameters, "ID_sity"); + _сity = jsonReadStr(parameters, "сity"); + _lon = jsonReadStr(parameters, "lon"); + _lat = jsonReadStr(parameters, "lat"); + _lang = jsonReadStr(parameters, "lang"); + _param = jsonReadStr(parameters, "param"); + jsonRead(parameters, "debug", _debug); + long interval; + jsonRead(parameters, F("int"), interval); // в минутах + setInterval(interval * 60); + } + + void getWeather() + { + String ret; + + if (isNetworkActive()) + { + String urlReq; + if (_сity != "") + { + // Для нового API + // request = "http://api.openweathermap.org/geo/1.0/direct?q=" _сity + "&limit=1&appid=" + _API_key; + // Устарело, но пока работает + urlReq = "http://api.openweathermap.org/data/2.5/weather?q=" + _сity + "&units=metric&lang=" + _lang + "&appid=" + _API_key; + } + else + { + // Получаем название города по координатам. Новый API + // request = "http://api.openweathermap.org/geo/1.0/reverse?lat=" + _lat + "&lon=" + _lon + "&limit=1&appid=" + _API_key; + //[0].local_names:{ru:Москва,...} + urlReq = "http://api.openweathermap.org/data/2.5/weather?lat=" + _lat + "&lon=" + _lon + "&units=metric&lang=" + _lang + "&appid=" + _API_key; + } + + WiFiClient client; + HTTPClient http; + String payload; + bool find = false; + http.setTimeout(500); + http.begin(client, urlReq); // urlCurrent + // http.begin(client, "http://api.openweathermap.org/data/2.5/weather?id=" + _ID_sity + "&appid=" + _API_key + "&units=metric"); + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + int httpCode = http.GET(); + + if (httpCode > 0) + { + ret = httpCode; + + if (httpCode == HTTP_CODE_OK) + { + payload = http.getString(); + + deserializeJson(Weatherdoc1, payload); + ret += payload; + } + } + else + { + ret = http.errorToString(httpCode).c_str(); + } + if (_debug) + { + SerialPrint("<-", F("getOwmWeather"), urlReq); + SerialPrint("->", F("getOwmWeather"), "server: " + ret); + } + http.end(); + } + } + + void doByInterval() + { + + getWeather(); + if (jsonReadStr(Weatherdoc1["main"], "temp", true) != "null") + { + + publishNew("main", "temp"); + publishNew("main", "pressure"); + publishNew("main", "humidity"); + publishNew("wind", "speed"); + publishNew("wind", "deg"); + publishNew("clouds", "all"); + publishNew("weather", "main"); + publishNew("weather", "description"); + publishNew("weather", "icon"); + publishNew("sys", "sunrise"); + publishNew("sys", "sunset"); + publishNew("", "name"); + + if (_param == "temp") + { + value.valS = jsonReadStr(Weatherdoc1["main"], "temp", true); + } + else if (_param == "pressure") + { + value.valS = jsonReadStr(Weatherdoc1["main"], "pressure", true); + } + else if (_param == "humidity") + { + value.valS = jsonReadStr(Weatherdoc1["main"], "humidity", true); + } + else if (_param == "speed") + { + value.valS = jsonReadStr(Weatherdoc1["wind"], "speed", true); + } + else if (_param == "deg") + { + value.valS = jsonReadStr(Weatherdoc1["wind"], "deg", true); + } + else if (_param == "all") + { + value.valS = jsonReadStr(Weatherdoc1["clouds"], "all", true); + } + else if (_param == "main") + { + value.valS = jsonReadStr(Weatherdoc1["weather"][0], "main", true); + } + else if (_param == "description") + { + value.valS = jsonReadStr(Weatherdoc1["weather"][0], "description", true); + } + else if (_param == "icon") + { + value.valS = jsonReadStr(Weatherdoc1["weather"][0], "icon", true); + } + else if (_param == "sunrise") + { + value.valS = getTimeDotFormatedFromUnix(std::atoll(jsonReadStr(Weatherdoc1["sys"], "sunrise", true).c_str())); + } + else if (_param == "sunset") + { + value.valS = getTimeDotFormatedFromUnix(std::atoll(jsonReadStr(Weatherdoc1["sys"], "sunset", true).c_str())); + } + else if (_param == "sunset") + { + value.valS = Weatherdoc1["name"].as(); + } + // value.isDecimal = false; + + regEvent(value.valS, "owmWeather"); + } + } + + IoTValue execute(String command, std::vector ¶m) + { + if (command == "get") + { + // getWeather(); + doByInterval(); + } + + return {}; + } + + // проверяем если пришедшее значение отличается от предыдущего регистрируем событие + static void publishNew(String root, String param) + { + IoTItem *tmp = findIoTItem("wea_" + param); + if (!tmp) + return; + + if (root == "weather") + { + if (Weatherdoc1[root][0][param].as() != tmp->value.valS) + { + tmp->setValue(Weatherdoc1[root][0][param].as(), true); + } + } + else if (root == "") + { + if (Weatherdoc1[param].as() != tmp->value.valS) + { + tmp->setValue(Weatherdoc1[param].as(), true); + } + } + else if (root == "sys") + { + if (Weatherdoc1[root][param].as() != tmp->value.valS) + { + tmp->setValue(getTimeDotFormatedFromUnix(std::atoll(jsonReadStr(Weatherdoc1[root], param, true).c_str())), true); + } + } + else + { + if (Weatherdoc1[root][param].as() != tmp->value.valS) + { + tmp->setValue(Weatherdoc1[root][param].as(), true); + } + } + } + + ~owmWeather(){}; +}; + +void *getAPI_owmWeather(String subtype, String param) +{ + if (subtype == F("owmWeather")) + { + return new owmWeather(param); + } + else + { + return nullptr; + } +}