Блог

Yandex.Weather компонент погоды для Home-assistant

(lapatoc)

В чате просили поделиться компонентом погоды для Yandex. Собственно делюсь.

В кабинете разработчика необходимо создать API ключ и выбрать тариф "Погода на вашем сайте".

Установка: содайте папку <hass config>/custom_components/yandex_weather. В папке содайте файл weather.py. Скопируйте код ниже в файл:

""" Support for the Yandex.Weather with “Weather on your site” rate. For more details about Yandex.Weather, please refer to the documentation at https://tech.yandex.com/weather/ """ import asyncio import logging import socket import aiohttp import async_timeout from datetime import timedelta import voluptuous as vol import homeassistant.util.dt as dt_util from homeassistant.const import ( CONF_NAME, CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, TEMP_CELSIUS, STATE_UNKNOWN) from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, ATTR_FORECAST_PRECIPITATION, ATTR_FORECAST_WIND_BEARING, ATTR_FORECAST_WIND_SPEED, ATTR_WEATHER_PRESSURE, ATTR_WEATHER_HUMIDITY, PLATFORM_SCHEMA, WeatherEntity) from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util import Throttle import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) TIME_STR_FORMAT = '%H:%M %d.%m.%Y' DEFAULT_NAME = 'Yandex Weather' ATTRIBUTION = 'Data provided by Yandex.Weather' ATTR_FEELS_LIKE = 'feels_like' ATTR_WEATHER_ICON = 'weather_icon' ATTR_PRESSURE_MM = 'pressure_mm' ATTR_OBS_TIME = 'observation_time' ATTR_WEATHER_CON = 'weather_condition' ATTR_PRECIPITATION_PROB = 'precipitation_probability' ATTR_WIND_SPEED_MS = 'wind_speed_ms' CONDITION_CLASSES = { 'sunny': ['clear'], 'partlycloudy': ['partly-cloudy'], 'cloudy': ['cloudy', 'overcast'], 'pouring': ['overcast-and-rain'], 'rainy': ['cloudy-and-rain', 'overcast-and-light-rain', 'cloudy-and-light-rain', 'partly-cloudy-and-light-rain'], 'lightning-rainy': ['overcast-thunderstorms-with-rain'], 'snowy-rainy': ['overcast-and-wet-snow'], 'snowy': ['cloudy-and-snow', 'overcast-and-light-snow', 'cloudy-and-light-snow', 'overcast-and-snow', 'partly-cloudy-and-snow', 'partly-cloudy-and-light-snow'], } DESCRIPTION_DIC = { 'clear': 'Ясно', 'partly-cloudy': 'Малооблачно', 'cloudy': 'Облачно с прояснениями', 'overcast': 'Пасмурно', 'partly-cloudy-and-light-rain': 'Небольшой дождь', 'partly-cloudy-and-rain': 'Дождь', 'overcast-and-rain': 'Сильный дождь', 'overcast-thunderstorms-with-rain': 'Сильный дождь, гроза', 'cloudy-and-light-rain': 'Небольшой дождь', 'overcast-and-light-rain': 'Небольшой дождь', 'cloudy-and-rain': 'Дождь', 'overcast-and-wet-snow': 'Дождь со снегом', 'partly-cloudy-and-light-snow': 'Небольшой снег', 'partly-cloudy-and-snow': 'Снег', 'overcast-and-snow': 'Снегопад', 'cloudy-and-light-snow': 'Небольшой снег', 'overcast-and-light-snow': 'Небольшой снег', 'cloudy-and-snow': 'Снег', } MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Required(CONF_API_KEY): cv.string, vol.Optional(CONF_LATITUDE): cv.latitude, vol.Optional(CONF_LONGITUDE): cv.longitude, }) async def async_setup_platform( hass, config, async_add_entities, discovery_info=None): """Set up the Yandex.Weather weather platform.""" longitude = config.get(CONF_LONGITUDE, hass.config.longitude) latitude = config.get(CONF_LATITUDE, hass.config.latitude) name = config.get(CONF_NAME) api_key = config.get(CONF_API_KEY) session = async_get_clientsession(hass) loop = hass.loop async_add_entities([YandexWeather(name, longitude, latitude, api_key, loop, session)], True) class YandexWeather (WeatherEntity): """Representation of a weather entity.""" def __init__(self, name: str, longitude: str, latitude: str, api_key: str, loop, session): self._name = name self._longitude = longitude self._latitude = latitude self._api_key = api_key self._hloop = loop self._hsession = session self._weather_data = YaWeather(self._latitude, self._longitude, self._api_key, self._hloop, session=self._hsession) @Throttle(MIN_TIME_BETWEEN_UPDATES) async def async_update(self): """Get the latest weather information.""" await self._weather_data.get_weather() @property def name(self) -> str: """Return the name of the sensor.""" return self._name @property def temperature(self) -> int: """Return the temperature.""" if self._weather_data.current is not None: return self._weather_data.current['temp'] return None @property def temperature_unit(self) -> str: """Return the unit of measurement.""" return TEMP_CELSIUS @property def humidity(self) -> int: """Return the humidity.""" if self._weather_data.current is not None: return self._weather_data.current['humidity'] return None @property def wind_speed(self) -> float: """Return the wind speed.""" if self._weather_data.current is not None: # Convert from m/s to km/h return round(self._weather_data.current['wind_speed'] * 18 / 5) return None @property def wind_bearing(self) -> str: """Return the wind speed.""" if self._weather_data.current is not None: # The current wind bearing return self._weather_data.current['wind_dir'] return None @property def pressure(self) -> int: """Return the pressure.""" if self._weather_data.current is not None: return self._weather_data.current['pressure_pa'] return None @property def condition(self) -> str: if self._weather_data.current is not None: return next(( k for k, v in CONDITION_CLASSES.items() if self._weather_data.current['condition'] in v), None) return STATE_UNKNOWN @property def condition_icon(self) -> int: """Return the pressure.""" if self._weather_data.current is not None: return self._weather_data.current['icon'] return None @property def forecast(self): """Return the forecast array.""" if self._weather_data.forecast is not None: fcdata_out = [] for data_in in self._weather_data.forecast['parts']: data_out = {} if (self._weather_data.forecast['parts'].index(data_in) == 0): data_out[ATTR_FORECAST_TIME] = dt_util.utcnow()+timedelta(minutes=350) if (self._weather_data.forecast['parts'].index(data_in) == 1): data_out[ATTR_FORECAST_TIME] = dt_util.utcnow()+timedelta(minutes=700) data_out[ATTR_FORECAST_TEMP] = data_in['temp_max'] data_out[ATTR_FORECAST_TEMP_LOW] = data_in['temp_min'] data_out[ATTR_FORECAST_CONDITION] = next(( k for k, v in CONDITION_CLASSES.items() if data_in['condition'] in v), None) data_out[ATTR_PRESSURE_MM] = data_in['pressure_mm'] data_out[ATTR_WEATHER_ICON] = data_in['icon'] data_out[ATTR_FEELS_LIKE] = data_in['feels_like'] data_out[ATTR_FORECAST_WIND_SPEED] = round(data_in['wind_speed'] * 18 / 5) data_out[ATTR_FORECAST_WIND_BEARING] = data_in['wind_dir'] data_out[ATTR_FORECAST_PRECIPITATION] = data_in['prec_mm'] data_out[ATTR_PRECIPITATION_PROB] = data_in['prec_prob'] data_out[ATTR_WEATHER_CON] = DESCRIPTION_DIC[data_in['condition']] data_out[ATTR_WEATHER_PRESSURE] = data_in['pressure_pa'] data_out[ATTR_WEATHER_HUMIDITY] = data_in['humidity'] data_out[ATTR_WIND_SPEED_MS] = data_in['wind_speed'] data_out['part_of_day'] = data_in['part_name'] fcdata_out.append(data_out) return fcdata_out @property def attribution(self) -> str: """Return the attribution.""" return ATTRIBUTION @property def device_state_attributes(self): """Return device specific state attributes.""" if self._weather_data.current is not None: data = dict() data[ATTR_FEELS_LIKE] = self._weather_data.current['feels_like'] data[ATTR_PRESSURE_MM] = self._weather_data.current['pressure_mm'] data[ATTR_WIND_SPEED_MS] = self._weather_data.current['wind_speed'] data[ATTR_WEATHER_ICON] = self._weather_data.current['icon'] data[ATTR_OBS_TIME] = dt_util.as_local(dt_util.utc_from_timestamp(self._weather_data.current['obs_time'])).strftime(TIME_STR_FORMAT) data[ATTR_WEATHER_CON] = DESCRIPTION_DIC[self._weather_data.current['condition']] return data return None class YaWeather(object): """A class for returning Yandex Weather data.""" def __init__(self, lat: str, lon: str, api_key, loop, session='none'): """Initialize the class.""" self._api = api_key self._lat = lat self._lon = lon self._loop = loop self._session = session self._current = {} self._forecast = {} async def get_weather(self): base_url="https://api.weather.yandex.ru/v1/informers?lat=%s&lon=%s" % (self._lat, self._lon) headers = {'X-Yandex-API-Key':self._api} try: async with async_timeout.timeout(5, loop=self._loop): response = await self._session.get(base_url, headers=headers) data = await response.json() if ('status' not in data): self._current = data['fact'] self._forecast = data['forecast'] else: _LOGGER.error('Error fetching data from Yandex.Weather, %s, %s', data['status'],data['message']) except (asyncio.TimeoutError, aiohttp.ClientError, socket.gaierror) as error: _LOGGER.error('Error fetching data from Yandex.Weather, %s', error) @property def forecast(self): """Return forecast""" return self._forecast @property def current(self): """Return curent condition""" return self._current

В конфиге добавьте следующее:

weather: - platform: yandex_weather api_key: <Ваш API ключа>

У меня компонент работает уже пару месяцев. Тестировал только с бесплатным ключом. Предоставляется AS IS.

Стандартная карточка выглядит неочень мягко говоря для прогноза на два периода. По этому лучше использовать кастомную карточку:

https://github.com/kalkih/simple-weather-card

Выглядеть будет вот так:

Update 03.12.19

Наконец дошли руки выложить код на Github.

Рабочий код и инструкция по установке лежит здесь.


Отлично! Добавить еще бы скриншотик как выглядит в НА



Выглядит со стандартной карточкой убого. Использую кастомную - simple weather card 

https://github.com/kalkih/simple-weather-card">https://github.com/kalkih/simp...

1000x_image.png?1555066655

А где брать API ?

А что значит бесплатный ключ, беспланый ключ нет такого понятия в разделе разработчика яндекс. Если не сложно дополните как на 

https://developer.tech.yandex.ru/keys/">https://developer.tech.yandex какой пункт выбирать?

Тестовый. Он имеет ограничение на число запросов в сутки. 

Спасибо огромное)

Тестовый ключ работает месяц, потом нужно перейти на тариф "погода на вашем сайте" 

Плагин погоды: 

https://github.com/naofireblade/homebridge-weather-plus">https://github.com/naofireblade/homebridge-weather-plus


В Homekit отображает текущую температуру и можно добавить прогрозы на несколько дней. В Eve отображает еще кучу параметров и рисует графики.

Error fetching data from Yandex.Weather, 403, Forbidden




к API сразу доступ выдает, или ждать надо? Не работает чет

Надо ждать. До суток могут включать.



api вроде работает


1000x_image.png?1555268505

но ошибка так и есть 




2019-04-14 21:06:35 ERROR (MainThread) [homeassistant.core] Error doing job: Task exception was never retrieved Traceback (most recent call last): File "/usr/local/lib/python3.7/site-packages/homeassistant/helpers/entity.py", line 225, in async_update_ha_state self._async_write_ha_state() File "/usr/local/lib/python3.7/site-packages/homeassistant/helpers/entity.py", line 248, in _async_write_ha_state state = self.state File "/usr/local/lib/python3.7/site-packages/homeassistant/components/weather/__init__.py", line 173, in state return self.condition File "/config/custom_components/yandex_weather/weather.py", line 168, in condition if self._weather_data.current['condition'] in v), None) File "/config/custom_components/yandex_weather/weather.py", line 168, in if self._weather_data.current['condition'] in v), None) KeyError: 'condition'

Тоже уже 3-й день 

Error fetching data from Yandex.Weather, 403, Forbidden

:( Чет не получается докрутить....

Такая же самая беда, надеюсь кто решит отпишется. 

Будьте добры, для особо одаренных, покажите запрос с ключом в одну строку (или через curl).


Попробую распарсить в openHAB.

Пример есть в документации яндекс

https://tech.yandex.com/weather/doc/dg/concepts/forecast-info-docpage/">https://tech.yandex.com/weathe...

Мне больше зашла карточка type: weather-forecast, красивая и больше данный показывает

Дополню.
1. В новых версиях HA надо добавить в папку /custom_components/yandex_weather пустой файл с именем _init_.py

2. Если в логах видите ошибку, значит в Yandex.Weather API у вас стоит план "Testing", а не "Weather on your site". И тут есть два пути:

  2.1 Сменить план в https://developer.tech.yandex.ru/services/">кабинете разработчика для Yandex.Weather API

 2.2 Если хотите дальше использовать план "Testing" - заменить в файле weather.py в 245 строке адрес  на https://api.weather.yandex.ru/v1/forecast/">https://api.weather.yandex.ru/v1/forecast/  Напомню, что план "Testing" работает всего 30 дней. 

Error fetching data from Yandex.Weather, 403, Forbidden

Спасибо! Получилось запустить на новой версии HA, только имя файла должно быть: __init__.py

Помимо добавления пустого файла
__init__.py

пришлось для проверки корректности конфигурации без ошибок добавить в папку /custom_components/yandex_weather

ещё и
файл
manifest.json
. Без него проверка давала сообщение об ошибке "Integration yandex_weather not found when trying to verify its weather platform."


Содержание файла:


{
"domain": "Yandex Weather",
"name": "Yandex Weather",
"documentation": "https://sprut.ai/client/blog/1165",
"requirements": [],
"dependencies": [],
"codeowners": ["@sprut.ai"]
}


https://community.home-assistant.io/t/integration-zanzito-not-found-when-trying-to-verify-its-notify-platform/113393">пример решения подобной ошибки



сделал все как в инструкции и дополнительно создал пустые файлы (по совету из последних комментариев) в папке Yandex_weather, в итоге ничего не работает:

1000x_image.png?1567716567

Все работает. Единственный вопрос: подскажите, как отображать прогноз на несколько дней вперед? По умолчанию только 2 дня.

Бесплатный ключ, который дает возможность получить прогноз на несколько дней живет только 30 дней. Далее его нужно покупать. Стоит очень недружественно. По этому я не реализовывал эту функциональность. Компонент поддерживает работу только по тарифному плану "Погода на вашем сайте" - сути вперед.

Platform error weather.yandex_weather - Integration 'yandex_weather' not found. 

Помогите решить ошибку все файлы я добавил !!

Что сделал ни так

Попробуйте взять компонент из https://github.com/bastshoes/yandex_weather">github.

Удалось запустить?

Тоже положил в папку два файла с github и один создал

manifest.json, как тут выше советовали.

В итоге - 

Platform error weather.yandex_weather - Integration 'yandex_weather' not found.

У кого-нибудь получилось это вылечить?

видимо никто  и не лечил) тоже и файлы создавал, и с Git брал - а ошибка одна и та же. 

Добавить получилось, ошибки нет. Но в Панель разработчики - Состояние погода от Яндекса не появилась. Подскажите что не так сделал? 

так же не работает... Platform error weather.yandex_weather - Integration 'yandex_weather' not found

эти ошибки лечаться, но смысла не имеет. 


1) вам на месяц дадут всего 50 запросов (а их несколько в день прилетает)


2) форкаст будет на 1,5 дня вперед




В сапорт яндекса я писал и результат такой:


Если хочется больше, то нужно оформлять подписку на юр.лицу. Физики идуи в лес. 



Есть мод для Гисметео - ушел на него. и форкасты длинные и короткие есть и никаких лимитов по запросам

50 запросов в сутки, а не в месяц. Вроде должно хватать.
Подскажете, как вылечить эти ошибки? Есть init и manifest уже, но все равно



Configuration invalid
error weather.yandex_weather - Integration 'yandex_weather' not found.


тоже была такая ошибка,
полечилось перезагрузкой сервера.
после создания custom_components нужно его бутать

No 'version' key in the manifest file for custom integration 'yandex_weather'. This will not be allowed in a future version of Home Assistant. Please report this to the maintainer of 'yandex_weather'

Прошел квест на последней сборке Home Assistant (core-2021.4.6 / 5.13).


Берем весь репозиторий отсюда https://github.com/bastshoes/yandex_weather/tree/master">GitHub - bastshoes/yandex_weather: HomeAssistant custom component for Yandex Weather , никаких файлов отдельно не создаем.

Получаем API ключ "Погода на сайте". Ключ в конфигурейшн.ямл (хотя у меня он отдельно в другой расшаренной папке висит в weather.yaml напару с настройками для ГисМетео) указываем без угловых скобок.




Привет. 
На этой же сборе - не получается. Скачал файлы с Гитхаба зип.архивом, разархивировал, по одному закинул вct файлы в /config/custom_components/yandex_weather 


https://sprut.ai/static/media/cache/00/83/48/5/8334668/77944/1000x_image.jpg?1620230609" alt="1000x_image.jpg?1620230609" />

в логах такое:

https://sprut.ai/static/media/cache/00/83/48/5/8334724/77946/1000x_image.jpg?1620231573" alt="1000x_image.jpg?1620231573" />

плашка в Lovelace появилась, но пустая:


https://sprut.ai/static/media/cache/00/83/48/5/8334668/77945/1000x_image.jpg?1620230662" alt="1000x_image.jpg?1620230662" />

Сразу и не появляется, видимо Яндекс АПИ долго подтверждает после регистрации.

Сейчас-то все заработало?

Да, завелось. Сорян за панику :)



Сравнение 4 источников. Яндекс страннее всех, кроме того, что во Вторник показывает доп дни Вторник+Вторник (иногда второй день все-таки бывает "следующий"), так и ещё всего 2 дня.

https://sprut.ai/static/media/cache/00/60/69/5/8394107/78082/1000x_image.png?1620721512" alt="1000x_image.png?1620721512" />

А Гисметео где взять не ткнёте в туториал?

Объект weather.yandex_weather появился. Карточку

Simple Weather Card

установил через HACS, но не могу разобраться, как данные яндекса загнать в эту карточку. Есть гденить для начинающих пошаговая ;) ?

Параметры настройки карточки там же на гитхаб. 

https://github.com/kalkih/simple-weather-card">https://github.com/kalkih/simp...



Да, спасибо. Вроде разобрался :)

проблема была в том, что в инструкции указано добавить в /config/ui-lovelace.yaml

resources:
- url: /local/simple-weather-card-bundle.js?v=0.8.2
type: module

а при установке указано

resources:
- url: /hacsfiles/simple-weather-card/simple-weather-card-bundle.js
type: module

переустановил, увидел, исправил - заработало.

Может кому пригодится :)

Подскажите, можно ли на основании этой интеграции сенсор создать с температурой, например, с обновлением раз в 30 минут?


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