Глядя на красивый и удобный вывод информации о погоде у Яндекса и у MSN
Захотелось тоже самое сделать у себя, чтобы на планшете у входной двери отображалась такая информация (кроме непосредственно управлением умным домом).
Судя по комментариям, меня не совсем правильно поняли, статья написано не о том как подтянуть погоду в Home Assistant (для этого есть как встроенные интеграции так и сторонние, я, например, использую Gismeteo), а о том как у себя отобразить в пару строк информацию, которая нужна перед выходом на улицу: есть ли дождь, будет ли дождь вечером, стоит ли взять зонт, какую одежду надеть и тп. Такой информации в официальных API нет.
Не уверен есть ли необходимая информация в API (думаю, что свободного вообще нет), решил автоматизировать получение этой полезной информации через плагин к HA - Node-RED. Предполагается, что он у вас уже установлен.
Весь код на NR будет в самом конце статьи.
В итоге "графически" алгоритмы получения информации с сайта Yandex выглядит так:
С сайта MSN так:
Первым делом нам нужно в html коде сайта Яндекс.Погоды найти селектор, в который выводится нужная нам информация:
Селектором оказывается div c классом title-icon__text
В рабочую область с панели инструментов кидаем узел http request и настраиваем его на запрос по url яндекс погоды, подставляя координаты из HA.
Я еще добавил, на всякий случай, User agent, так как за частые однотипные запросы Яндекс отдает страницу с капчей.
Следом кидаем узел отвечающий за обработку данных полученных с предыдущего узла, в поле Selector указываем найденный ранее селектор. В Output указываем что нам нужен только чистый текст.
В итоге по всем найденным селекторам будет сформирован поток данных, который нужно объединить в одну строку для вывода в будущий виртуальный датчик HA, делаем это с помощью узла Join
И последний важный узел это виртуальный датчик Home Assistant, куда будет отправлена сформированная ранее строка.
Тут важный параметр Entity config, нужно выбрать Add new ha-entity, после деплоя кода в области видимости сенсоров HA появится новый с транслитерированным названием.
Как вы наверно заметили, на первой картинке гораздо больше узлов, их я специально пропустил чтобы не загромождать статью. Они отвечают за периодичность запросов и дебаг, а также я добавил второй сенсор, у которого другой селектор.
В итоге в панели управления умным домом эта информация будет выглядеть примерно так:
Для получения информации с сайта MSN все очень похоже, за исключением того что "удобного селектора" я не нашел; само название параметра и его значение выбираются двумя разными селекторами:
Это div.lifeIndexBarTitle-E1_1 и div.lifeIndexBarSummary-E1_1 мне пришлось их объединять в один поток с помощью стороннего узла Merge (он ставится в пару кликов в NR: Menu, Manage palette, Install).
Так как два разных потока формируют результат асинхронно, нужен Merge, который после получения первого сигнала ждет (по умолчанию) две секунды для получения всех остальных, а потом объединенный поток отправляет дальше.
А дальше для нашего нового виртуального сенсора, нужно из этого большого объекта (внутри два потока) получить строку для вывода, для этого нам нужен узел Function (который может выполнять код на JS):
var array1 = msg.payload[0].payload;
var array2 = msg.payload[1].payload;
var newArray = "";
for (var i = 0; i < array1.length && i < array2.length; i++) {
newArray += array1[i] + ": " + array2[i]+"\r\n";
}
msg.payload = newArray;
return msg;
Пример вывода нового сенсора от MSN:
И результат полностью:
Коды ниже можно импортировать непосредственно в Node-RED, скопировав в буфер и вставив.
Код для Яндекс
[{"id":"ad1d92815f2810e3","type":"tab","label":"Осадки от Yandex","disabled":false,"info":"","env":[]},{"id":"91b656e34bb36636","type":"inject","z":"ad1d92815f2810e3","name":"каждый 15 минут","props":[{"p":"payload"}],"repeat":"900","crontab":"","once":true,"onceDelay":"5","topic":"","payload":"","payloadType":"date","x":130,"y":60,"wires":[["c2a6fc2cee844617","d147070d4e892acb"]]},{"id":"c2a6fc2cee844617","type":"delay","z":"ad1d92815f2810e3","name":"Случайная задержка","pauseType":"random","timeout":"30","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"10","randomLast":"50","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":380,"y":60,"wires":[["33244b375f9536ba"]]},{"id":"d147070d4e892acb","type":"http request","z":"ad1d92815f2810e3","name":"Запрос осадков сейчас","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://yandex.ru/pogoda/maps/nowcast?lat={{states.zone.home.attributes.latitude}}&lon={{states.zone.home.attributes.longitude}}","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[{"keyType":"other","keyValue":"user-agent","valueType":"other","valueValue":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"}],"x":170,"y":160,"wires":[["b83ef7431f50c2b9","088f18771b04025a"]]},{"id":"b83ef7431f50c2b9","type":"html","z":"ad1d92815f2810e3","name":"Выбираем строку с прогнозом","property":"payload","outproperty":"payload","tag":"div.weather-maps-fact__nowcast-alert","ret":"text","as":"single","x":430,"y":160,"wires":[["d5f87c9849faccbe"]]},{"id":"33244b375f9536ba","type":"http request","z":"ad1d92815f2810e3","name":"Запрос осадков на неделю","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://yandex.ru/pogoda/?lat={{states.zone.home.attributes.latitude}}&lon={{states.zone.home.attributes.longitude}}","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":160,"y":260,"wires":[["725a40f983bcb9ed","088f18771b04025a"]]},{"id":"725a40f983bcb9ed","type":"html","z":"ad1d92815f2810e3","name":"Выбираем строку с прогнозом","property":"payload","outproperty":"payload","tag":"div.title-icon__text","ret":"text","as":"single","x":430,"y":260,"wires":[["faec5435d1fd53df"]]},{"id":"088f18771b04025a","type":"html","z":"ad1d92815f2810e3","name":"Попали на SmartCaptcha?","property":"payload","outproperty":"payload","tag":"span.Text_typography_headline-s","ret":"text","as":"single","x":420,"y":360,"wires":[["079500ac2cd1bdb3"]]},{"id":"079500ac2cd1bdb3","type":"debug","z":"ad1d92815f2810e3","name":"SmartCaptcha","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":660,"y":360,"wires":[]},{"id":"0760bb2f15fda35b","type":"ha-sensor","z":"ad1d92815f2810e3","name":"Осадки сейчас","entityConfig":"e90834b91730474e","version":0,"state":"payload","stateType":"msg","attributes":[{"property":"update","value":"updated","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":940,"y":160,"wires":[[]]},{"id":"1b430bc67266fc46","type":"ha-sensor","z":"ad1d92815f2810e3","name":"Осадки на неделю","entityConfig":"079e8fb6221f5071","version":0,"state":"payload","stateType":"msg","attributes":[{"property":"update","value":"updated","valueType":"msg"}],"inputOverride":"allow","outputProperties":[],"x":950,"y":260,"wires":[[]]},{"id":"c8f660c6f44cbf00","type":"join","z":"ad1d92815f2810e3","name":"Объединение в строку","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"1","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":710,"y":260,"wires":[["1b430bc67266fc46"]]},{"id":"d5f87c9849faccbe","type":"join","z":"ad1d92815f2810e3","name":"Объединение в строку","mode":"custom","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"1","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":710,"y":160,"wires":[["0760bb2f15fda35b"]]},{"id":"faec5435d1fd53df","type":"sort","z":"ad1d92815f2810e3","name":"","order":"ascending","as_num":false,"target":"payload","targetType":"msg","msgKey":"payload","msgKeyType":"elem","seqKey":"payload","seqKeyType":"msg","x":570,"y":300,"wires":[["c8f660c6f44cbf00"]]},{"id":"e90834b91730474e","type":"ha-entity-config","server":"31f480d3.0e3b28","deviceConfig":"","name":"sensor config for Осадки сейчас","version":6,"entityType":"sensor","haConfig":[{"property":"name","value":"Осадки сейчас"},{"property":"device_class","value":""},{"property":"icon","value":"mdi:weather-pouring"},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"resend":true},{"id":"079e8fb6221f5071","type":"ha-entity-config","server":"31f480d3.0e3b28","deviceConfig":"","name":"sensor config for Осадки на неделю","version":6,"entityType":"sensor","haConfig":[{"property":"name","value":"Осадки на неделю"},{"property":"device_class","value":""},{"property":"icon","value":"mdi:weather-pouring"},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""},{"property":"last_reset","value":""}],"resend":true},{"id":"31f480d3.0e3b28","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]
Код для MSN
[{"id":"19cbf5e1e88f2024","type":"tab","label":"Зонт от MSN","disabled":false,"info":"","env":[]},{"id":"ab5ae602979d5186","type":"inject","z":"19cbf5e1e88f2024","name":"каждый 15 минут","props":[{"p":"payload"}],"repeat":"900","crontab":"","once":true,"onceDelay":"5","topic":"","payload":"","payloadType":"date","x":130,"y":80,"wires":[["7fb408618d93a612"]]},{"id":"7fb408618d93a612","type":"delay","z":"19cbf5e1e88f2024","name":"Случайная задержка","pauseType":"random","timeout":"30","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":360,"y":80,"wires":[["de2ef8c68437fb20"]]},{"id":"de2ef8c68437fb20","type":"http request","z":"19cbf5e1e88f2024","name":"Запрос осадков сейчас","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://www.msn.com/ru-ru/weather/life/in-Москва,Россия","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[{"keyType":"other","keyValue":"user-agent","valueType":"other","valueValue":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"}],"x":150,"y":180,"wires":[["cd3d04497b69e8b9","dafc373cb1e4ef54"]]},{"id":"cd3d04497b69e8b9","type":"html","z":"19cbf5e1e88f2024","name":"Выбираем характеристики","property":"payload","outproperty":"payload","tag":"div.lifeIndexBarTitle-E1_1","ret":"text","as":"single","x":460,"y":140,"wires":[["e2748d35141c91e0","30fda68a6944851f"]]},{"id":"dafc373cb1e4ef54","type":"html","z":"19cbf5e1e88f2024","name":"Выбираем значения характеристик","property":"payload","outproperty":"payload","tag":"div.lifeIndexBarSummary-E1_1","ret":"text","as":"single","x":490,"y":200,"wires":[["e2748d35141c91e0","30fda68a6944851f"]]},{"id":"30fda68a6944851f","type":"debug","z":"19cbf5e1e88f2024","name":"debug 1","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":950,"y":300,"wires":[]},{"id":"e2748d35141c91e0","type":"merge","z":"19cbf5e1e88f2024","name":"","timeout":"2","x":730,"y":160,"wires":[["d03b49b3583b8fea"]]},{"id":"d03b49b3583b8fea","type":"function","z":"19cbf5e1e88f2024","name":"Объединение значений","func":"var array1 = msg.payload[0].payload;\nvar array2 = msg.payload[1].payload;\n\nvar newArray = \"\";\n for (var i = 0; i < array1.length && i < array2.length; i++) {\n newArray += array1[i] + \": \" + array2[i]+\"\\r\\n\";\n }\nmsg.payload = newArray;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":930,"y":160,"wires":[["30fda68a6944851f","eca2fbc2f5eb3a41"]]},{"id":"eca2fbc2f5eb3a41","type":"ha-sensor","z":"19cbf5e1e88f2024","name":"Одежда по погоде","entityConfig":"628079d19b822784","version":0,"state":"payload","stateType":"msg","attributes":[],"inputOverride":"allow","outputProperties":[],"x":1050,"y":60,"wires":[[]]},{"id":"628079d19b822784","type":"ha-entity-config","server":"31f480d3.0e3b28","deviceConfig":"","name":"lifeBar","version":"6","entityType":"sensor","haConfig":[{"property":"name","value":"Одежда по погоде"},{"property":"icon","value":""},{"property":"entity_category","value":""},{"property":"entity_picture","value":""},{"property":"device_class","value":""},{"property":"unit_of_measurement","value":""},{"property":"state_class","value":""}],"resend":false,"debugEnabled":false},{"id":"31f480d3.0e3b28","type":"server","name":"Home Assistant","version":5,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m","enableGlobalContextStore":true}]
Указанным методом можно извлекать любые данные с любых сайтов у которых нет официального API (или платное) и использовать в автоматизации умного дома.
У этого решения есть существенный недостаток - если дизайн сайта изменится, в новых датчиках мы получим пустой результат и придется искать новый селектор и править код.