Files
homeassistant/config/custom_components/heweather/weather.py
T
2026-08-24 21:55:33 +08:00

612 lines
25 KiB
Python

import logging
from datetime import datetime, timedelta
import time
import asyncio
import async_timeout
import aiohttp
import voluptuous as vol
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.weather import (
WeatherEntity,
WeatherEntityFeature,
Forecast,
ATTR_FORECAST_CONDITION,
ATTR_FORECAST_NATIVE_TEMP,
ATTR_FORECAST_NATIVE_TEMP_LOW,
ATTR_FORECAST_TIME,
ATTR_FORECAST_HUMIDITY,
ATTR_FORECAST_NATIVE_PRECIPITATION,
ATTR_FORECAST_WIND_BEARING,
ATTR_FORECAST_NATIVE_WIND_SPEED,
PLATFORM_SCHEMA)
from homeassistant.const import (
ATTR_ATTRIBUTION,
CONF_MODE,
#LENGTH_KILOMETERS,
UnitOfLength,
#PRESSURE_HPA,
UnitOfPressure,
#SPEED_KILOMETERS_PER_HOUR,
UnitOfSpeed,
#PRECIPITATION_MILLIMETERS_PER_HOUR,
UnitOfVolumetricFlux,
#TEMP_CELSIUS
UnitOfTemperature
)
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
from .heweather.const import (
DOMAIN,
CONF_AUTH_METHOD,
CONF_LONGITUDE,
CONF_LATITUDE,
CONF_HOST,
CONF_KEY,
CONF_JWT_SUB,
CONF_JWT_KID,
DEFAULT_HOST,
CONDITION_CLASSES,
ATTR_UPDATE_TIME,
ATTRIBUTION
)
_LOGGER = logging.getLogger(__name__)
TIME_BETWEEN_UPDATES = timedelta(seconds=1800)
HOURLY_TIME_BETWEEN_UPDATES = timedelta(seconds=1800)
DEFAULT_TIME = dt_util.now()
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_LONGITUDE): cv.string,
vol.Required(CONF_LATITUDE): cv.string,
vol.Required(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Required(CONF_KEY): cv.string,
})
# # 集成安装
# async def async_setup_entry(hass, config_entry, async_add_entities):
# _LOGGER.debug(f"register_static_path: {ROOT_PATH + ':custom_components/qweather/local'}")
# hass.http.register_static_path(ROOT_PATH, hass.config.path('custom_components/qweather/local'), False)
# hass.components.frontend.add_extra_js_url(hass, ROOT_PATH + '/qweather-card/qweather-card.js?ver=' + VERSION)
# hass.components.frontend.add_extra_js_url(hass, ROOT_PATH + '/qweather-card/qweather-more-info.js?ver=' + VERSION)
#
# _LOGGER.info("setup platform weather.Heweather...")
#
# name = config_entry.data.get(CONF_NAME)
# key = config_entry.data[CONF_API_KEY]
# location = config_entry.data[CONF_LOCATION]
# #unique_id = config_entry.unique_id
# longitude = round(config_entry.data[CONF_LONGITUDE],2)
# latitude = round(config_entry.data[CONF_LATITUDE],2)
# update_interval_minutes = config_entry.options.get(CONF_UPDATE_INTERVAL, 10)
# dailysteps = config_entry.options.get(CONF_DAILYSTEPS, 7)
# if dailysteps != 7 and dailysteps !=3:
# dailysteps = 7
# hourlysteps = config_entry.options.get(CONF_HOURLYSTEPS, 24)
# if hourlysteps != 24:
# hourlysteps = 24
# alert = config_entry.options.get(CONF_ALERT, True)
# life = config_entry.options.get(CONF_LIFEINDEX, True)
# starttime = config_entry.options.get(CONF_STARTTIME, 0)
# gird_weather = config_entry.options.get(CONF_GIRD, False)
#
# #data = WeatherData(hass, name, unique_id, api_key, longitude, latitude, dailysteps ,hourlysteps, alert, life, starttime, gird_weather)
# #location = config.get(CONF_LOCATION)
# #key = config.get(CONF_KEY)
# data = WeatherData(hass, location, key)
# await data.async_update(dt_util.now())
# async_track_time_interval(hass, data.async_update, timedelta(minutes = update_interval_minutes))
# _LOGGER.debug('[%s]刷新间隔时间: %s 分钟', name, update_interval_minutes)
# async_add_entities([HeWeather(data, location)], True)
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback):
"""Set up the hefeng weather."""
_LOGGER.info("setup platform weather.Heweather...")
longitude = config_entry.data.get(CONF_LONGITUDE)
latitude = config_entry.data.get(CONF_LATITUDE)
host = config_entry.data.get(CONF_HOST)
auth_method = config_entry.data.get(CONF_AUTH_METHOD)
if auth_method == "key":
key = config_entry.data.get(CONF_KEY)
data = WeatherData(hass, longitude, latitude, host, key=key)
else:
# HeWeather Certification
heweather_cert = hass.data[DOMAIN].get('heweather_cert', None)
jwt_sub = config_entry.data.get(CONF_JWT_SUB)
jwt_kid = config_entry.data.get(CONF_JWT_KID)
data = WeatherData(hass, longitude, latitude, host, heweather_cert=heweather_cert, jwt_sub=jwt_sub, jwt_kid=jwt_kid)
weather = HeWeather(data, longitude, latitude)
await weather.async_update_data(dt_util.now())
config_entry.async_on_unload(async_track_time_interval(hass, weather.async_update_data, TIME_BETWEEN_UPDATES, cancel_on_shutdown=True))
async_add_entities([weather], True)
#@asyncio.coroutine
async def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the hefeng weather."""
_LOGGER.info("setup platform weather.Heweather...")
longitude = config.get(CONF_LONGITUDE)
latitude = config.get(CONF_LATITUDE)
host = config.get(CONF_HOST)
key = config.get(CONF_KEY)
data = WeatherData(hass, longitude, latitude, host, key=key)
weather = HeWeather(data, longitude, latitude)
await weather.async_update_data(dt_util.now())
async_track_time_interval(hass, weather.async_update_data, TIME_BETWEEN_UPDATES, cancel_on_shutdown=True)
async_add_devices([weather], True)
class HeWeather(WeatherEntity):
"""Representation of a weather condition."""
_attr_native_temperature_unit = UnitOfTemperature.CELSIUS
_attr_native_precipitation_unit = UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR
_attr_native_pressure_unit = UnitOfPressure.HPA
_attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
_attr_native_visibility_unit = UnitOfLength.KILOMETERS
def __init__(self, data, longitude, latitude):
"""Initialize the weather."""
# 【新增】开启实体命名支持
self._attr_has_entity_name = True
# 【新增】设置英文名,这样生成的 ID 就是 weather.home_heweather
self._attr_name = "heweather"
#self._name = None
self._object_id = 'localweather'
self._condition = None
self._temperature = None
self._humidity = None
self._pressure = None
self._wind_speed = None
self._wind_bearing = None
self._visibility = None
self._precipitation = None
self._forecast = None
self._forecast_hourly = None
self._dew = None
self._feelslike = None
self._cloud =None
self._data = data
self._updatetime = None
self._attr_unique_id = f"localweather_{longitude}_{latitude}"
self._attr_supported_features = 0
self._attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
self._attr_supported_features |= WeatherEntityFeature.FORECAST_HOURLY
@property
def device_info(self):
"""Return the device info."""
return {
"identifiers": {(DOMAIN, f"{self._data._params['location']}")},
"name": "和风天气",
"manufacturer": "QWeather",
"model": "API v7",
}
# @property
# def name(self):
# """返回实体的名字."""
# return '和风天气'
@property
def should_poll(self):
"""attention No polling needed for a demo weather condition."""
return True
@property
def native_dew_point(self):
"""Return the native_dew_point."""
return self._dew
@property
def native_apparent_temperature(self):
"""Return the native_apparent_temperature."""
return self._feelslike
@property
def cloud_coverage(self):
"""Return the cloud_coverage."""
return self._cloud
@property
def native_temperature(self):
"""Return the temperature."""
return self._temperature
@property
def native_temperature_unit(self):
"""Return the unit of measurement."""
return self._attr_native_temperature_unit
@property
def humidity(self):
"""Return the humidity."""
return self._humidity
@property
def native_wind_speed(self):
"""Return the wind speed."""
return self._wind_speed
@property
def wind_bearing(self):
"""Return the wind speed."""
return self._wind_bearing
@property
def native_pressure(self):
"""Return the pressure."""
return self._pressure
@property
def native_visibility(self):
"""Return the visibility."""
return self._visibility
@property
def native_precipitation(self):
"""Return the precipitation."""
return self._precipitation
@property
def condition(self):
"""Return the weather condition."""
if self._condition:
match_list = [k for k, v in CONDITION_CLASSES.items() if self._condition in v]
return match_list[0] if match_list else 'unknown'
else:
return 'unknown'
# @property
# def attribution(self):
# """Return the attribution."""
# return 'Powered by Home Assistant'
# @property
# def device_state_attributes(self):
# """设置其它一些属性值."""
# if self._condition is not None:
# return {
# ATTR_ATTRIBUTION: ATTRIBUTION,
# ATTR_UPDATE_TIME: self._updatetime
# }
async def async_forecast_daily(self) -> list[Forecast]:
"""Return the daily forecast."""
# reftime = datetime.now()
reftime = self._data._updatetime
forecast_data = []
for entry in self._forecast:
data_dict = {
ATTR_FORECAST_TIME: reftime.isoformat(),
ATTR_FORECAST_CONDITION: entry[0],
ATTR_FORECAST_NATIVE_TEMP: entry[1],
ATTR_FORECAST_NATIVE_TEMP_LOW: entry[2],
'text': entry[3]
}
reftime = reftime + timedelta(days=1)
forecast_data.append(data_dict)
return forecast_data
async def async_forecast_hourly(self) -> list[Forecast]:
"""Return the daily forecast."""
# reftime = datetime.now()
reftime = self._data._updatetime
forecast_hourly_data = []
for entry in self._forecast_hourly:
data_dict = {
ATTR_FORECAST_TIME: reftime.isoformat(),
ATTR_FORECAST_CONDITION: entry[0],
ATTR_FORECAST_NATIVE_TEMP: entry[1],
ATTR_FORECAST_HUMIDITY: entry[2],
#"native_precipitation": entry[3],
ATTR_FORECAST_WIND_BEARING: entry[4],
ATTR_FORECAST_NATIVE_WIND_SPEED: entry[5],
"precipitation_probability": entry[6],
'text': entry[7]
}
#[forecast_hourly[0], float(hourlymsg[0]["temp"]), float(hourlymsg[0]["humidity"]), float(hourlymsg[0]["precip"]), hourlymsg[0]["windDir"], int(hourlymsg[0]["windSpeed"])],
reftime = reftime + timedelta(hours=1)
forecast_hourly_data.append(data_dict)
return forecast_hourly_data
#@asyncio.coroutine
async def async_update(self, now=DEFAULT_TIME):
"""update函数变成了async_update."""
self._updatetime = self._data.updatetime
#self._name = self._data.name
self._condition = self._data.condition
self._temperature = self._data.temperature
_attr_native_temperature_unit = self._data.temperature_unit
self._humidity = self._data.humidity
self._pressure = self._data.pressure
self._wind_speed = self._data.wind_speed
self._wind_bearing = self._data.wind_bearing
self._visibility = self._data.visibility
self._precipitation = self._data.precipitation
self._dew = self._data.dew
self._feelslike = self._data.feelslike
self._cloud = self._data.cloud
self._forecast = self._data.forecast
self._forecast_hourly = self._data.forecast_hourly
_LOGGER.info("success to update informations")
async def async_update_data(self, now=DEFAULT_TIME):
await self._data.async_update(now)
await self.async_update_listeners(['daily', 'hourly'])
class WeatherData():
"""天气相关的数据,存储在这个类中."""
def __init__(self, hass, longitude, latitude, host, key=None, heweather_cert=None, jwt_sub=None, jwt_kid=None):
"""初始化函数."""
self._hass = hass
location = f"{longitude},{latitude}"
if key is not None:
self._is_jwt = False
self._forecast_url = "https://"+host+"/v7/weather/7d?location="+location+"&key="+key
self._weather_now_url = "https://"+host+"/v7/weather/now?location="+location+"&key="+key
self._forecast_hourly_url = "https://"+host+"/v7/weather/24h?location="+location+"&key="+key
self._params = {"location": location,
"key": key}
else:
self._is_jwt = True
self._forecast_url = "https://"+host+"/v7/weather/7d?location="+location
self._weather_now_url = "https://"+host+"/v7/weather/now?location="+location
self._forecast_hourly_url = "https://"+host+"/v7/weather/24h?location="+location
self._params = {"location": location}
self._heweather_cert = heweather_cert
self._jwt_sub = jwt_sub
self._jwt_kid = jwt_kid
#self._name = None
self._condition = None
self._temperature = None
self._temperature_unit = None
self._humidity = None
self._pressure = None
self._wind_speed = None
self._wind_bearing = None
self._visibility = None
self._precipitation = None
self._dew = None
self._feelslike = None
self._cloud =None
self._forecast = None
self._forecast_hourly = None
self._updatetime = None
@property
def condition(self):
"""天气情况."""
return self._condition
@property
def temperature(self):
"""温度."""
return self._temperature
@property
def temperature_unit(self):
"""温度单位."""
return UnitOfTemperature.CELSIUS
@property
def humidity(self):
"""湿度."""
return self._humidity
@property
def pressure(self):
"""气压."""
return self._pressure
@property
def dew(self):
"""露点温度"""
return self._dew
@property
def feelslike(self):
"""体感温度"""
return self._feelslike
@property
def cloud(self):
"""云量"""
return self._cloud
@property
def wind_speed(self):
"""风速."""
return self._wind_speed
@property
def wind_bearing(self):
"""风向."""
return self._wind_bearing
@property
def visibility(self):
"""能见度."""
return self._visibility
@property
def precipitation (self):
"""当前小时累计降水量."""
return self._precipitation
@property
def forecast(self):
"""天预报."""
return self._forecast
@property
def forecast_hourly(self):
"""小时预报."""
return self._forecast_hourly
@property
def updatetime(self):
"""更新时间."""
return self._updatetime
#@asyncio.coroutine
async def async_update(self, now):
"""从远程更新信息."""
_LOGGER.info("Update from JingdongWangxiang's OpenAPI...")
"""
# 异步模式的测试代码
import time
_LOGGER.info("before time.sleep")
time.sleep(40)
_LOGGER.info("after time.sleep and before asyncio.sleep")
asyncio.sleep(40)
_LOGGER.info("after asyncio.sleep and before yield from asyncio.sleep")
#yield from
await asyncio.sleep(40)
_LOGGER.info("after yield from asyncio.sleep")
"""
# 通过HTTP访问,获取需要的信息
# 此处使用了基于aiohttp库的async_get_clientsession
try:
timeout = aiohttp.ClientTimeout(total=20)
connector = aiohttp.TCPConnector(limit=10)
headers = None
if self._is_jwt:
jwt_token = await self._heweather_cert.get_jwt_token_heweather_async(self._jwt_sub, self._jwt_kid, int(time.time()) - 30, int(time.time()) + 180)
headers = {'Authorization': f'Bearer {jwt_token}'}
async with aiohttp.ClientSession(connector=connector, timeout=timeout, headers=headers) as session:
async with session.get(self._weather_now_url) as response:
json_data = await response.json()
weather = json_data["now"]
async with session.get(self._forecast_url) as response:
json_data = await response.json()
forecast = json_data
async with session.get(self._forecast_hourly_url) as response:
json_data = await response.json()
forecast_hourly = json_data
except(asyncio.TimeoutError, aiohttp.ClientError):
_LOGGER.error("Error while accessing: %s", self._weather_now_url)
_LOGGER.error("Error while accessing: %s", self._forecast_url)
_LOGGER.error("Error while accessing: %s", self._forecast_hourly_url)
return
self._temperature = float(weather["temp"])
self._humidity = float(weather["humidity"])
self._pressure = weather["pressure"]
self._condition = weather["text"]
self._wind_speed = weather["windSpeed"]
self._wind_bearing = weather["windDir"]
self._visibility = weather["vis"]
self._precipitation = float(weather["precip"])
self._feelslike = float(weather["feelsLike"])
self._dew = float(weather["dew"])
self._cloud = int(weather["cloud"])
# self._windScale = weather["windScale"]
self._updatetime = datetime.strptime(weather["obsTime"], "%Y-%m-%dT%H:%M%z")
datemsg = forecast["daily"]
forec_cond = []
forec_text = []
for n in range(7):
for i, j in CONDITION_CLASSES.items():
if datemsg[n]["textDay"] in j:
forec_cond.append(i)
forec_text.append(datemsg[n]["textDay"])
self._forecast = [
[forec_cond[0], int(datemsg[0]["tempMax"]), int(datemsg[0]["tempMin"]), forec_text[0]],
[forec_cond[1], int(datemsg[1]["tempMax"]), int(datemsg[1]["tempMin"]), forec_text[1]],
[forec_cond[2], int(datemsg[2]["tempMax"]), int(datemsg[2]["tempMin"]), forec_text[2]],
[forec_cond[3], int(datemsg[3]["tempMax"]), int(datemsg[3]["tempMin"]), forec_text[3]],
[forec_cond[4], int(datemsg[4]["tempMax"]), int(datemsg[4]["tempMin"]), forec_text[4]],
[forec_cond[5], int(datemsg[5]["tempMax"]), int(datemsg[5]["tempMin"]), forec_text[5]],
[forec_cond[6], int(datemsg[6]["tempMax"]), int(datemsg[6]["tempMin"]), forec_text[6]]
]
hourlymsg = forecast_hourly["hourly"]
forecast_hourly = []
forec_text = []
for n in range(24):
for i, j in CONDITION_CLASSES.items():
if hourlymsg[n]["text"] in j:
forecast_hourly.append(i)
forec_text.append(hourlymsg[n]["text"])
self._forecast_hourly = [
[forecast_hourly[0], float(hourlymsg[0]["temp"]), float(hourlymsg[0]["humidity"]), float(hourlymsg[0]["precip"]), hourlymsg[0]["windDir"], int(hourlymsg[0]["windSpeed"]), float(hourlymsg[0]["pop"]), forec_text[0]],
[forecast_hourly[1], float(hourlymsg[1]["temp"]), float(hourlymsg[1]["humidity"]), float(hourlymsg[1]["precip"]), hourlymsg[1]["windDir"], int(hourlymsg[1]["windSpeed"]), float(hourlymsg[1]["pop"]), forec_text[1]],
[forecast_hourly[2], float(hourlymsg[2]["temp"]), float(hourlymsg[2]["humidity"]), float(hourlymsg[2]["precip"]), hourlymsg[2]["windDir"], int(hourlymsg[2]["windSpeed"]), float(hourlymsg[2]["pop"]), forec_text[2]],
[forecast_hourly[3], float(hourlymsg[3]["temp"]), float(hourlymsg[3]["humidity"]), float(hourlymsg[3]["precip"]), hourlymsg[3]["windDir"], int(hourlymsg[3]["windSpeed"]), float(hourlymsg[3]["pop"]), forec_text[3]],
[forecast_hourly[4], float(hourlymsg[4]["temp"]), float(hourlymsg[4]["humidity"]), float(hourlymsg[4]["precip"]), hourlymsg[4]["windDir"], int(hourlymsg[4]["windSpeed"]), float(hourlymsg[4]["pop"]), forec_text[4]],
[forecast_hourly[5], float(hourlymsg[5]["temp"]), float(hourlymsg[5]["humidity"]), float(hourlymsg[5]["precip"]), hourlymsg[5]["windDir"], int(hourlymsg[5]["windSpeed"]), float(hourlymsg[5]["pop"]), forec_text[5]],
[forecast_hourly[6], float(hourlymsg[6]["temp"]), float(hourlymsg[6]["humidity"]), float(hourlymsg[6]["precip"]), hourlymsg[6]["windDir"], int(hourlymsg[6]["windSpeed"]), float(hourlymsg[6]["pop"]), forec_text[6]],
[forecast_hourly[7], float(hourlymsg[7]["temp"]), float(hourlymsg[7]["humidity"]), float(hourlymsg[7]["precip"]), hourlymsg[7]["windDir"], int(hourlymsg[7]["windSpeed"]), float(hourlymsg[7]["pop"]), forec_text[7]],
[forecast_hourly[8], float(hourlymsg[8]["temp"]), float(hourlymsg[8]["humidity"]), float(hourlymsg[8]["precip"]), hourlymsg[8]["windDir"], int(hourlymsg[8]["windSpeed"]), float(hourlymsg[8]["pop"]), forec_text[8]],
[forecast_hourly[9], float(hourlymsg[9]["temp"]), float(hourlymsg[9]["humidity"]), float(hourlymsg[9]["precip"]), hourlymsg[9]["windDir"], int(hourlymsg[9]["windSpeed"]), float(hourlymsg[9]["pop"]), forec_text[9]],
[forecast_hourly[10], float(hourlymsg[10]["temp"]), float(hourlymsg[10]["humidity"]), float(hourlymsg[10]["precip"]), hourlymsg[10]["windDir"], int(hourlymsg[10]["windSpeed"]), float(hourlymsg[10]["pop"]), forec_text[10]],
[forecast_hourly[11], float(hourlymsg[11]["temp"]), float(hourlymsg[11]["humidity"]), float(hourlymsg[11]["precip"]), hourlymsg[11]["windDir"], int(hourlymsg[11]["windSpeed"]), float(hourlymsg[11]["pop"]), forec_text[11]],
[forecast_hourly[12], float(hourlymsg[12]["temp"]), float(hourlymsg[12]["humidity"]), float(hourlymsg[12]["precip"]), hourlymsg[12]["windDir"], int(hourlymsg[12]["windSpeed"]), float(hourlymsg[12]["pop"]), forec_text[12]],
[forecast_hourly[13], float(hourlymsg[13]["temp"]), float(hourlymsg[13]["humidity"]), float(hourlymsg[13]["precip"]), hourlymsg[13]["windDir"], int(hourlymsg[13]["windSpeed"]), float(hourlymsg[13]["pop"]), forec_text[13]],
[forecast_hourly[14], float(hourlymsg[14]["temp"]), float(hourlymsg[14]["humidity"]), float(hourlymsg[14]["precip"]), hourlymsg[14]["windDir"], int(hourlymsg[14]["windSpeed"]), float(hourlymsg[14]["pop"]), forec_text[14]],
[forecast_hourly[15], float(hourlymsg[15]["temp"]), float(hourlymsg[15]["humidity"]), float(hourlymsg[15]["precip"]), hourlymsg[15]["windDir"], int(hourlymsg[15]["windSpeed"]), float(hourlymsg[15]["pop"]), forec_text[15]],
[forecast_hourly[16], float(hourlymsg[16]["temp"]), float(hourlymsg[16]["humidity"]), float(hourlymsg[16]["precip"]), hourlymsg[16]["windDir"], int(hourlymsg[16]["windSpeed"]), float(hourlymsg[16]["pop"]), forec_text[16]],
[forecast_hourly[17], float(hourlymsg[17]["temp"]), float(hourlymsg[17]["humidity"]), float(hourlymsg[17]["precip"]), hourlymsg[17]["windDir"], int(hourlymsg[17]["windSpeed"]), float(hourlymsg[17]["pop"]), forec_text[17]],
[forecast_hourly[18], float(hourlymsg[18]["temp"]), float(hourlymsg[18]["humidity"]), float(hourlymsg[18]["precip"]), hourlymsg[18]["windDir"], int(hourlymsg[18]["windSpeed"]), float(hourlymsg[18]["pop"]), forec_text[18]],
[forecast_hourly[19], float(hourlymsg[19]["temp"]), float(hourlymsg[19]["humidity"]), float(hourlymsg[19]["precip"]), hourlymsg[19]["windDir"], int(hourlymsg[19]["windSpeed"]), float(hourlymsg[19]["pop"]), forec_text[19]],
[forecast_hourly[20], float(hourlymsg[20]["temp"]), float(hourlymsg[20]["humidity"]), float(hourlymsg[20]["precip"]), hourlymsg[20]["windDir"], int(hourlymsg[20]["windSpeed"]), float(hourlymsg[20]["pop"]), forec_text[20]],
[forecast_hourly[21], float(hourlymsg[21]["temp"]), float(hourlymsg[21]["humidity"]), float(hourlymsg[21]["precip"]), hourlymsg[21]["windDir"], int(hourlymsg[21]["windSpeed"]), float(hourlymsg[21]["pop"]), forec_text[21]],
[forecast_hourly[22], float(hourlymsg[22]["temp"]), float(hourlymsg[22]["humidity"]), float(hourlymsg[22]["precip"]), hourlymsg[22]["windDir"], int(hourlymsg[22]["windSpeed"]), float(hourlymsg[22]["pop"]), forec_text[22]],
[forecast_hourly[23], float(hourlymsg[23]["temp"]), float(hourlymsg[23]["humidity"]), float(hourlymsg[23]["precip"]), hourlymsg[23]["windDir"], int(hourlymsg[23]["windSpeed"]), float(hourlymsg[23]["pop"]), forec_text[23]]
]
_LOGGER.info("success to load local informations")