Статья

Node-RED. Как сделать свой прогноз погоды и получить его push-уведомлением?

Захотелось создать свой собственный парк развлечений, с блэкджеком и шлюхами прогноз погоды.

Теперь каждый день в 10:00 я получаю push-уведомление на телефон с содержанием: 

Сегодня в Вт, 21 янв 
Мин темп в 09:26 = -25 
Макс темп в 16:01 = -17 

================ 

Завтра в Ср, 22 янв 
Мин темп в 02:58 = -25 
Макс темп в 22:03 = -7 

================ 

Послезавтра в Чт, 23 янв 
Мин темп в 07:06 = -9 
Макс темп в 23:00 = -2

Используемые инструменты:

Имеющаяся система автоматизации 

node-RED.

Погодные данные. 

Решение

Для прозрачности и наглядности получения прогноза погоды, а также минимизации глюков со стороны сторонних палитр (есть готовые ноды для DarkSky), я воспользовался описанием запроса, который может возвращать данные для любой даты: https://darksky.net/dev/docs#time-machine-request.

(Вообще, можно послать обычный запрос и вытащить дневные данные из массива [daily], но из-за разности UTC и локального времени, нужно создавать условия для определения - когда наступило завтра, а когда еще нет, что делает такое решение непрозрачным и путанным для меня).

URL web-запроса получается такой: 

https://api.darksky.net/forecast/[key]/[latitude],[longitude],[time]?exclude=[blocks]&units=[units]

где:

  • [key] - полученный после регистрации на сайте Secret Key (доступен в разделе "Консоль");
  • [latitude],[longitude] - ваши геокоординаты либо того места, для которого нужен прогноз;
  • [time] - UNIX время в секундах, либо в текстовом формате [YYYY]-[MM]-[DD]T[HH]:[MM]:[SS][timezone];
  • exclude=[blocks] - исключение ненужных блоков возвращаемых данных для уменьшения размера передаваемых данных (полезно для ускорения запроса, если имеется узкий интернет-канал);
  • units=[units] - формат возвращаемых данных. Например, чужая имперская мера измерений (Фаренгейты и мили) или наша родная СИ (Цельсии и метры в секунду). 

Для формирование URL я использовал ноды "function", отдельно формируя время в кол-ве секунд, разное для "сегодня", "завтра" и "послезавтра".

Работа инициируется запросом от одной ноды inject, повторяющегося каждый день в 13:00 локального времени.

Для нод "0"/"1"/"2" код:  

msg.payload = 0; return msg;

где: 0 или 1, или 2 = кол-во дней вперед. Например, 1 - завтра, 2 - послезавтра.

Для ноды "date+" код: 

var date = new Date(); date.setHours( 10,0,0,0 ); var amount = msg.payload; //===== var tzOff = date.getTimezoneOffset() * 60 * 1000, t = date.getTime(), d = new Date(), tzOff2; t += (1000 * 60 * 60 * 24) * amount; d.setTime(t); tzOff2 = date.getTimezoneOffset() * 60 * 1000; if (tzOff != tzOff2) { var diff = tzOff2 - tzOff; t += diff; d.setTime(t); } //===== msg.payload = d.getTime() / 1000; return msg;

Для ноды "url date+" код: 

msg.url= "https://api.darksky.net/forecast/"+ "8c436809881364415cb1ba009316be79/"+ "55.018803,82.933952,"+ msg.payload+ "?units=si&exclude=hourly,flags"; return msg;

Итогом для этих трех нод будет URL, который отправляется через ноду "http request" обычным методом GET:

На выходе получается строка, которую я преобразую в json соответствующей нодой "json":

Дальше мне нужно собрать данные от нескольких дней (сегодня, завтра, послезавтра), что можно сделать нодой "join", но перед сбором эти данные нужно подготовить. 

1. Нужно выделить нужные данные (зачем мне видимость в километрах?). 

Мне нужны были:  

минимальная температура в нужные сутки,время этой минимальной температуры,максимальная температура в нужные сутки,время этой максимальной температуры.


2. Нужно обозначить соответственные топики для этих данных, т.к. ноде "join" необходима разметка. 

минимальная температура в нужные сутки = Min,время этой минимальной температуры = MinTime,максимальная температура в нужные сутки = Max,время этой максимальной температуры = MaxTime.


3. Нужно преобразовать данные в желаемый мной формат.

Пункты 1 и 2. 

Для выделения нужных данных и пометкой их "топиком", я использовал ноду "change". 

Содержания у …0, …1, …2 одинаковое, кроме суффикса (0 - сегодня, 1 - завтра, 2 - послезавтра). 

Обратите внимание, что с параметром времени - тупое определение заменено на небольшой код javascript из-за того, что darksky возвращал время сокращенно и нужно увеличивать параметр в 1000 раз, чтобы получить общепринятые секунды (так и не понял, в чем дело). 

Преобразование полученных данных производится мной в нодах "function". 

Температуру округляю (зачем мне сотые доли?). 

Дату превращаю в простую строку, содержащую только желаемую информацию (зачем мне год?)

Для ноды "Round" код: 

msg.payload = Math.round(msg.payload); return msg;

Для ноды "DateSec2DateString" код: 

var date = new Date(msg.payload); var weekday = new Array(7); weekday[0] = 'Пн'; weekday[1] = 'Вт'; weekday[2] = 'Ср'; weekday[3] = 'Чт'; weekday[4] = 'Пт'; weekday[5] = 'Сб'; weekday[6] = 'Вс'; var monthN = new Array(12); monthN[0] = 'янв'; monthN[1] = 'фев'; monthN[2] = 'мар'; monthN[3] = 'апр'; monthN[4] = 'май'; monthN[5] = 'июн'; monthN[6] = 'июл'; monthN[7] = 'авг'; monthN[8] = 'сен'; monthN[9] = 'окт'; monthN[10] = 'ноя'; monthN[11] = 'дек'; hour = date.getHours(); minute = date.getMinutes(); second = date.getSeconds(); day = date.getDate(); month = date.getMonth()+1; year = date.getFullYear()-2000; dayName = weekday[date.getDay()-1]; monthName = monthN[date.getMonth()]; dateString = dayName + ', ' + day.toString().padStart(2, '0') + ' ' + monthName; //month.toString().padStart(2, '0'); //year.toString().padStart(2, '0'); timeString = hour.toString().padStart(2, '0') + ':' + minute.toString().padStart(2, '0'); //second.toString().padStart(2, '0'); msg.payload = {}; msg.payload.general = dateString + " " + timeString; msg.payload.date = dateString; msg.payload.time = timeString; msg.payload.monthday = day.toString().padStart(2, '0'); msg.payload.dayname = dayName; msg.payload.month = month; msg.payload.monthname = monthName; msg.payload.year = year; msg.payload.hour = hour.toString().padStart(2, '0'); msg.payload.minute = minute.toString().padStart(2, '0'); return msg;

Теперь все это нужно объединить в кучу и преобразовать в нужный итоговый текст. 

Здесь мне нужны упомянутая ранее нода "join" и опять же, любимые ноды "function".

Нода "join" имеет следующие свойства:

где: 12 - кол-во данных, которое необходимо получить на входе, чтобы произвести объединение (если убрать из прогноза "сегодня", то таких данных будет 8. Соответственно, для одного дня это будет 4.).

Нода "function" перед нодой "join" - ничего не делает. Я использую ее только для визуальной стройности в node-red. 

После получения кучи данных в payload, их можно использовать так, как мне нужно. 

Для этого используются ноды типа "function" = "message" (где готовится текст сообщения) и "clear msg" (где я чищу msg… это уже перфекционизм). 

Для ноды "message" код:

var forecast=msg.payload; msg.payload= "Сегодня в "+forecast.MinTime0.date+"\n"+ "Мин темп в "+forecast.MinTime0.time+" = "+forecast.Min0+"\n"+ "Макс темп в "+forecast.MaxTime0.time+" = "+forecast.Max0+"\n"+ "\n================\n\n"+ "Завтра в "+forecast.MinTime1.date+"\n"+ "Мин темп в "+forecast.MinTime1.time+" = "+forecast.Min1+"\n"+ "Макс темп в "+forecast.MaxTime1.time+" = "+forecast.Max1+"\n"+ "\n================\n\n"+ "Послезавтра в "+forecast.MinTime2.date+"\n"+ "Мин темп в "+forecast.MinTime2.time+" = "+forecast.Min2+"\n"+ "Макс темп в "+forecast.MaxTime2.time+" = "+forecast.Max2+"\n"; return msg;

Для ноды "clear msg" код:

msg.topic = ""; delete msg.url; delete msg.statusCode; delete msg.headers; delete msg.responseUrl; delete msg.redirectList; return msg;

Почти всё! 

Теперь у меня есть текстовое сообщение на человеческом языке и я могу делать с ним, что угодно. Осталось передать его в телефон. 

Pushover - довольно знаменитый качественный сервис, позволяющий делать красиво оформленные уведомления. 

Для работы с ним нужно зарегистрироваться и создать token приложения (node-red в моем случае).

В самом node-red нужно установить палитру:

https://flows.nodered.org/node/node-red-contrib-pushover  

Настройки не сложные - нужно лишь указать user key и token приложения, полученные на сайте pushover.

Осталось подготовить служебную информацию для pushover:

Это я сделал нодой "function" = "pushover info" с кодом: 

msg.sound="none"; msg.topic="Прогноз на 2 дня"; return msg;

где: 

sound = звук, который должен издать телефон при получении ДАННОГО сообщения от pushover. Он может быть индивидуальным для каждого сообщения. 

Варианты: https://pushover.net/api#sounds

topic = заголовок уведомления в телефоне

Есть еще куча разных параметров, позволяющих создавать очень красивые насыщенные сообщения. Подробнее: https://pushover.net/api 

В ноде "pushover" ничего делать не нужно.

Как то так :) 

Полный код доступен на github (для удобства копирования нужно нажать кнопку RAW).

1

"Захотелось создать свой собственный парк развлечений, с блэкджеком и шлюхами прогноз погоды"

Это круто....)))))


Вернуться назад
Вернуться назад