初始化config文件
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"""The Colorfulclouds integration."""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_API_VERSION,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_NAME,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
CONF_ALERT,
|
||||
CONF_DAILYSTEPS,
|
||||
CONF_HOURLYSTEPS,
|
||||
CONF_STARTTIME,
|
||||
CONF_INTERVAL,
|
||||
COORDINATOR,
|
||||
DOMAIN,
|
||||
UNDO_UPDATE_LISTENER,
|
||||
)
|
||||
|
||||
from .colorfulclouds import ColorfulcloudsDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.WEATHER, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Set up Colorfulclouds as config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
api_key = config_entry.options.get(
|
||||
CONF_API_KEY
|
||||
) or config_entry.data.get(CONF_API_KEY, "")
|
||||
location_key = config_entry.unique_id
|
||||
longitude = config_entry.data[CONF_LONGITUDE]
|
||||
latitude = config_entry.data[CONF_LATITUDE]
|
||||
api_version = config_entry.options.get(
|
||||
CONF_API_VERSION
|
||||
) or config_entry.data.get(CONF_API_VERSION, "v2.5")
|
||||
dailysteps = config_entry.options.get(CONF_DAILYSTEPS, 5)
|
||||
hourlysteps = config_entry.options.get(CONF_HOURLYSTEPS, 24)
|
||||
alert = config_entry.options.get(CONF_ALERT, True)
|
||||
starttime = config_entry.options.get(CONF_STARTTIME, 0)
|
||||
interval = config_entry.options.get(CONF_INTERVAL, 10)
|
||||
websession = async_get_clientsession(hass)
|
||||
coordinator = ColorfulcloudsDataUpdateCoordinator(
|
||||
hass,
|
||||
websession,
|
||||
api_key,
|
||||
api_version,
|
||||
location_key,
|
||||
longitude,
|
||||
latitude,
|
||||
dailysteps,
|
||||
hourlysteps,
|
||||
alert,
|
||||
starttime,
|
||||
interval,
|
||||
)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
undo_listener = config_entry.add_update_listener(update_listener)
|
||||
hass.data[DOMAIN][config_entry.entry_id] = {
|
||||
COORDINATOR: coordinator,
|
||||
UNDO_UPDATE_LISTENER: undo_listener,
|
||||
}
|
||||
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
if unload_ok:
|
||||
entry_data = hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
entry_data[UNDO_UPDATE_LISTENER]()
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def update_listener(hass, config_entry):
|
||||
"""Update listener."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,97 @@
|
||||
"""Data update coordinator for the Colorfulclouds integration."""
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from aiohttp import ClientError
|
||||
from async_timeout import timeout
|
||||
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ColorfulcloudsDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching Colorfulclouds data API."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass,
|
||||
websession,
|
||||
api_key,
|
||||
api_version,
|
||||
location_key,
|
||||
longitude,
|
||||
latitude,
|
||||
dailysteps: int,
|
||||
hourlysteps: int,
|
||||
alert: bool,
|
||||
starttime: int,
|
||||
interval: int,
|
||||
):
|
||||
"""Initialize."""
|
||||
self.location_key = location_key
|
||||
self.longitude = longitude
|
||||
self.latitude = latitude
|
||||
self.dailysteps = dailysteps
|
||||
self.alert = alert
|
||||
self.interval = interval
|
||||
self.hourlysteps = hourlysteps
|
||||
self.api_version = api_version
|
||||
self.api_key = api_key
|
||||
self.starttime = starttime
|
||||
self.websession = websession
|
||||
self.is_metric = "metric:v2" if hass.config.units is METRIC_SYSTEM else "imperial"
|
||||
|
||||
update_interval = datetime.timedelta(minutes=self.interval)
|
||||
_LOGGER.debug("Data will be updated every %s", update_interval)
|
||||
|
||||
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
|
||||
|
||||
def _build_url(self, start_timestamp: int) -> str:
|
||||
"""Build the Colorfulclouds API URL."""
|
||||
return (
|
||||
f"https://api.caiyunapp.com/{self.api_version}/{self.api_key}/"
|
||||
f"{self.longitude},{self.latitude}/weather.json"
|
||||
f"?dailysteps={self.dailysteps}&hourlysteps={self.hourlysteps}"
|
||||
f"&alert={str(self.alert).lower()}&unit={self.is_metric}"
|
||||
f"×tamp={start_timestamp}"
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Update data via the Colorfulclouds API."""
|
||||
start_timestamp = int(
|
||||
(
|
||||
datetime.datetime.now() + datetime.timedelta(days=self.starttime)
|
||||
).timestamp()
|
||||
)
|
||||
url = self._build_url(start_timestamp)
|
||||
try:
|
||||
async with timeout(10):
|
||||
resp = await self.websession.get(url)
|
||||
resdata = await resp.json()
|
||||
except (ClientError, asyncio.TimeoutError) as error:
|
||||
raise UpdateFailed(
|
||||
f"Error communicating with Colorfulclouds API: {error}"
|
||||
) from error
|
||||
except ValueError as error:
|
||||
raise UpdateFailed(
|
||||
f"Invalid response from Colorfulclouds API: {error}"
|
||||
) from error
|
||||
|
||||
if resdata.get("status") != "ok":
|
||||
raise UpdateFailed(
|
||||
"Colorfulclouds API returned an error: "
|
||||
f"{resdata.get('error') or resdata.get('status')}"
|
||||
)
|
||||
|
||||
_LOGGER.debug("Colorfulclouds data refreshed successfully")
|
||||
return {
|
||||
**resdata,
|
||||
"location_key": self.location_key,
|
||||
"is_metric": self.is_metric,
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
"""Config flow for the Colorfulclouds integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
import logging
|
||||
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_API_VERSION,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_NAME,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .const import (
|
||||
CONF_ALERT,
|
||||
CONF_DAILYSTEPS,
|
||||
CONF_HOURLYSTEPS,
|
||||
CONF_STARTTIME,
|
||||
CONF_INTERVAL,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _fetch_api_data(url):
|
||||
"""Fetch and parse JSON from the given URL."""
|
||||
json_text = requests.get(url, timeout=10).content
|
||||
return json.loads(json_text)
|
||||
|
||||
|
||||
class ColorfulcloudsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Colorfulclouds."""
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for this handler."""
|
||||
return ColorfulcloudsOptionsFlow()
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize."""
|
||||
self._errors = {}
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the initial step."""
|
||||
self._errors = {}
|
||||
if user_input is None:
|
||||
return await self._show_config_form(user_input)
|
||||
|
||||
# Check if the location is already configured.
|
||||
existing = await self._check_existing(user_input[CONF_NAME])
|
||||
if existing:
|
||||
return self.async_abort(reason="already_configured")
|
||||
|
||||
# Test the API credentials.
|
||||
url = (
|
||||
f"https://api.caiyunapp.com/{user_input[CONF_API_VERSION]}/"
|
||||
f"{user_input[CONF_API_KEY]}/{user_input['longitude']},"
|
||||
f"{user_input['latitude']}/daily.json"
|
||||
)
|
||||
try:
|
||||
redata = await self.hass.async_add_executor_job(_fetch_api_data, url)
|
||||
except (requests.exceptions.RequestException, json.JSONDecodeError) as error:
|
||||
_LOGGER.warning("Unable to connect to Colorfulclouds API: %s", error)
|
||||
self._errors["base"] = "cannot_connect"
|
||||
return await self._show_config_form(user_input)
|
||||
|
||||
if redata.get("status") != "ok":
|
||||
self._errors["base"] = "communication"
|
||||
return await self._show_config_form(user_input)
|
||||
|
||||
await self.async_set_unique_id(
|
||||
f"{user_input['longitude']}-{user_input['latitude']}".replace(".", "_")
|
||||
)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
|
||||
|
||||
async def _show_config_form(self, user_input):
|
||||
"""Show the configuration form to the user."""
|
||||
# Defaults
|
||||
api_version = "v2.5"
|
||||
data_schema = OrderedDict()
|
||||
data_schema[vol.Required(CONF_API_KEY)] = str
|
||||
data_schema[vol.Optional(CONF_API_VERSION, default=api_version)] = str
|
||||
data_schema[
|
||||
vol.Optional(CONF_LONGITUDE, default=self.hass.config.longitude)
|
||||
] = cv.longitude
|
||||
data_schema[vol.Optional(CONF_LATITUDE, default=self.hass.config.latitude)] = (
|
||||
cv.latitude
|
||||
)
|
||||
data_schema[vol.Optional(CONF_NAME, default=self.hass.config.location_name)] = (
|
||||
str
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=vol.Schema(data_schema), errors=self._errors
|
||||
)
|
||||
|
||||
async def _check_existing(self, host):
|
||||
for entry in self._async_current_entries():
|
||||
if host == entry.data.get(CONF_NAME):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ColorfulcloudsOptionsFlow(config_entries.OptionsFlow):
|
||||
"""Config flow options for Colorfulclouds."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize."""
|
||||
self._errors = {}
|
||||
|
||||
@property
|
||||
def _api_key(self):
|
||||
"""Return the configured API key, options take precedence."""
|
||||
return self.config_entry.options.get(
|
||||
CONF_API_KEY
|
||||
) or self.config_entry.data.get(CONF_API_KEY, "")
|
||||
|
||||
@property
|
||||
def _api_version(self):
|
||||
"""Return the configured API version, options take precedence."""
|
||||
return self.config_entry.options.get(
|
||||
CONF_API_VERSION
|
||||
) or self.config_entry.data.get(CONF_API_VERSION, "v2.5")
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
return await self.async_step_user(user_input)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow initialized by the user."""
|
||||
self._errors = {}
|
||||
if user_input is not None:
|
||||
# Test the API credentials before saving.
|
||||
api_key = user_input.get(CONF_API_KEY) or self._api_key
|
||||
api_version = user_input.get(CONF_API_VERSION) or self._api_version
|
||||
url = (
|
||||
f"https://api.caiyunapp.com/{api_version}/{api_key}/"
|
||||
f"{self.config_entry.data[CONF_LONGITUDE]},"
|
||||
f"{self.config_entry.data[CONF_LATITUDE]}/daily.json"
|
||||
)
|
||||
try:
|
||||
redata = await self.hass.async_add_executor_job(_fetch_api_data, url)
|
||||
except (requests.exceptions.RequestException, json.JSONDecodeError) as error:
|
||||
_LOGGER.warning("Unable to connect to Colorfulclouds API: %s", error)
|
||||
self._errors["base"] = "cannot_connect"
|
||||
return await self._show_options_form(user_input)
|
||||
|
||||
if redata.get("status") != "ok":
|
||||
self._errors["base"] = "communication"
|
||||
return await self._show_options_form(user_input)
|
||||
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
return await self._show_options_form(user_input)
|
||||
|
||||
async def _show_options_form(self, user_input):
|
||||
"""Show the options form."""
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_API_KEY, default=self._api_key): str,
|
||||
vol.Optional(
|
||||
CONF_API_VERSION, default=self._api_version
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_DAILYSTEPS,
|
||||
default=self.config_entry.options.get(CONF_DAILYSTEPS, 5),
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=5, max=15)),
|
||||
vol.Optional(
|
||||
CONF_INTERVAL,
|
||||
default=self.config_entry.options.get(CONF_INTERVAL, 10),
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=5)),
|
||||
vol.Optional(
|
||||
CONF_HOURLYSTEPS,
|
||||
default=self.config_entry.options.get(CONF_HOURLYSTEPS, 24),
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=24, max=360)),
|
||||
vol.Optional(
|
||||
CONF_STARTTIME,
|
||||
default=self.config_entry.options.get(CONF_STARTTIME, 0),
|
||||
): vol.All(vol.Coerce(int), vol.Range(min=-5, max=0)),
|
||||
vol.Optional(
|
||||
CONF_ALERT,
|
||||
default=self.config_entry.options.get(CONF_ALERT, True),
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
errors=self._errors,
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
"""Constants for the colorfulClouds integration."""
|
||||
|
||||
DOMAIN = "colorfulclouds"
|
||||
|
||||
CONF_ALERT = "alert"
|
||||
CONF_HOURLYSTEPS = "hourlysteps"
|
||||
CONF_DAILYSTEPS = "dailysteps"
|
||||
CONF_STARTTIME = "starttime"
|
||||
CONF_INTERVAL = "interval"
|
||||
|
||||
COORDINATOR = "coordinator"
|
||||
|
||||
UNDO_UPDATE_LISTENER = "undo_update_listener"
|
||||
|
||||
ATTRIBUTION = "Data provided by Colorfulclouds"
|
||||
ATTR_ICON = "icon"
|
||||
ATTR_FORECAST = "forecast"
|
||||
ATTR_LABEL = "label"
|
||||
ATTR_UNIT_IMPERIAL = "Imperial"
|
||||
ATTR_UNIT_METRIC = "Metric"
|
||||
MANUFACTURER = "Colorfulclouds, Inc."
|
||||
NAME = "Colorfulclouds"
|
||||
|
||||
from homeassistant.const import ATTR_DEVICE_CLASS, DEGREE, UV_INDEX
|
||||
|
||||
OPTIONAL_SENSORS = ()
|
||||
SENSOR_TYPES = {
|
||||
"apparent_temperature": {
|
||||
ATTR_DEVICE_CLASS: "temperature",
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "感觉温度",
|
||||
ATTR_UNIT_METRIC: "°C",
|
||||
ATTR_UNIT_IMPERIAL: "°F",
|
||||
},
|
||||
"temperature": {
|
||||
ATTR_DEVICE_CLASS: "temperature",
|
||||
ATTR_ICON: None,
|
||||
ATTR_LABEL: "温度",
|
||||
ATTR_UNIT_METRIC: "°C",
|
||||
ATTR_UNIT_IMPERIAL: "°F",
|
||||
},
|
||||
"cloudrate": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-cloudy",
|
||||
ATTR_LABEL: "云量",
|
||||
ATTR_UNIT_METRIC: "%",
|
||||
ATTR_UNIT_IMPERIAL: "%",
|
||||
},
|
||||
"precipitation": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-rainy",
|
||||
ATTR_LABEL: "雨量",
|
||||
ATTR_UNIT_METRIC: "mm",
|
||||
ATTR_UNIT_IMPERIAL: "in",
|
||||
},
|
||||
"pressure": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:gauge",
|
||||
ATTR_LABEL: "气压",
|
||||
ATTR_UNIT_METRIC: "Pa",
|
||||
ATTR_UNIT_IMPERIAL: "Pa",
|
||||
},
|
||||
"comfort": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:gauge",
|
||||
ATTR_LABEL: "舒适指数",
|
||||
ATTR_UNIT_METRIC: None,
|
||||
ATTR_UNIT_IMPERIAL: None,
|
||||
},
|
||||
"ultraviolet": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-sunny",
|
||||
ATTR_LABEL: "紫外线",
|
||||
ATTR_UNIT_METRIC: UV_INDEX,
|
||||
ATTR_UNIT_IMPERIAL: UV_INDEX,
|
||||
},
|
||||
"humidity": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:water-percent",
|
||||
ATTR_LABEL: "湿度",
|
||||
ATTR_UNIT_METRIC: "%",
|
||||
ATTR_UNIT_IMPERIAL: "%",
|
||||
},
|
||||
"visibility": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-fog",
|
||||
ATTR_LABEL: "能见度",
|
||||
ATTR_UNIT_METRIC: "km",
|
||||
ATTR_UNIT_IMPERIAL: "mi",
|
||||
},
|
||||
"WindSpeed": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:weather-windy",
|
||||
ATTR_LABEL: "风速",
|
||||
ATTR_UNIT_METRIC: "km/h",
|
||||
ATTR_UNIT_IMPERIAL: "mph",
|
||||
},
|
||||
"WindDirection": {
|
||||
ATTR_DEVICE_CLASS: None,
|
||||
ATTR_ICON: "mdi:windsock",
|
||||
ATTR_LABEL: "风向",
|
||||
ATTR_UNIT_METRIC: DEGREE,
|
||||
ATTR_UNIT_IMPERIAL: DEGREE,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"domain": "colorfulclouds",
|
||||
"name": "colorfulClouds",
|
||||
"codeowners": ["@fineembs"],
|
||||
"config_flow": true,
|
||||
"dependencies": [],
|
||||
"documentation": "https://github.com/fineemb/Colorfulclouds-weather",
|
||||
"issue_tracker": "https://github.com/fineemb/Colorfulclouds-weather/issues",
|
||||
"homekit": {},
|
||||
"iot_class": "cloud_polling",
|
||||
"requirements": [],
|
||||
"version": "2.1.1",
|
||||
"ssdp": [],
|
||||
"zeroconf": []
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
"""Support for the Colorfulclouds service."""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, ATTR_DEVICE_CLASS, CONF_NAME
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import (
|
||||
ATTR_ICON,
|
||||
ATTR_LABEL,
|
||||
ATTRIBUTION,
|
||||
COORDINATOR,
|
||||
DOMAIN,
|
||||
MANUFACTURER,
|
||||
OPTIONAL_SENSORS,
|
||||
SENSOR_TYPES,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Add Colorfulclouds entities from a config_entry."""
|
||||
name = config_entry.data[CONF_NAME]
|
||||
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
|
||||
|
||||
sensors = []
|
||||
for sensor in SENSOR_TYPES:
|
||||
sensors.append(ColorfulcloudsSensor(name, sensor, coordinator))
|
||||
async_add_entities(sensors, False)
|
||||
|
||||
|
||||
class ColorfulcloudsSensor(Entity):
|
||||
"""Define an Colorfulclouds entity."""
|
||||
|
||||
def __init__(self, name, kind, coordinator, forecast_day=None):
|
||||
"""Initialize."""
|
||||
self._name = name
|
||||
self.kind = kind
|
||||
self.coordinator = coordinator
|
||||
self._device_class = None
|
||||
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._unit_system = (
|
||||
"Metric"
|
||||
if self.coordinator.data["is_metric"] == "metric:v2"
|
||||
else "Imperial"
|
||||
)
|
||||
self.forecast_day = forecast_day
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name."""
|
||||
if self.forecast_day is not None:
|
||||
return f"{self._name} {SENSOR_TYPES[self.kind][ATTR_LABEL]} {self.forecast_day}d"
|
||||
return f"{self._name} {SENSOR_TYPES[self.kind][ATTR_LABEL]}"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
# _LOGGER.info("sensor_unique_id: %s", self.coordinator.data["location_key"])
|
||||
return f"{self.coordinator.data['location_key']}-{self.kind}".lower()
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.coordinator.data["location_key"])},
|
||||
"name": self._name,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"entry_type": DeviceEntryType.SERVICE,
|
||||
}
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling requirement of the entity."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
return self.coordinator.last_update_success
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state."""
|
||||
try:
|
||||
realtime = self.coordinator.data["result"]["realtime"]
|
||||
if self.kind == "apparent_temperature":
|
||||
return realtime[self.kind]
|
||||
if self.kind == "pressure":
|
||||
return realtime[self.kind]
|
||||
if self.kind == "temperature":
|
||||
return realtime[self.kind]
|
||||
if self.kind == "humidity":
|
||||
return round(float(realtime[self.kind]) * 100)
|
||||
if self.kind == "cloudrate":
|
||||
return round(float(realtime[self.kind]) * 100)
|
||||
if self.kind == "visibility":
|
||||
return realtime[self.kind]
|
||||
if self.kind == "WindSpeed":
|
||||
return realtime["wind"]["speed"]
|
||||
if self.kind == "WindDirection":
|
||||
return realtime["wind"]["direction"]
|
||||
if self.kind == "comfort":
|
||||
return realtime["life_index"]["comfort"]["index"]
|
||||
if self.kind == "ultraviolet":
|
||||
return realtime["life_index"]["ultraviolet"]["index"]
|
||||
if self.kind == "precipitation":
|
||||
return realtime["precipitation"]["local"]["intensity"]
|
||||
except (KeyError, TypeError):
|
||||
return None
|
||||
return None
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
# if self.forecast_day is not None:
|
||||
# return FORECAST_SENSOR_TYPES[self.kind][ATTR_ICON]
|
||||
return SENSOR_TYPES[self.kind][ATTR_ICON]
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device_class."""
|
||||
# if self.forecast_day is not None:
|
||||
# return FORECAST_SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
|
||||
return SENSOR_TYPES[self.kind][ATTR_DEVICE_CLASS]
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit the value is expressed in."""
|
||||
# if self.forecast_day is not None:
|
||||
# return FORECAST_SENSOR_TYPES[self.kind][self._unit_system]
|
||||
return SENSOR_TYPES[self.kind][self._unit_system]
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
realtime = self.coordinator.data["result"]["realtime"]
|
||||
if self.kind == "ultraviolet":
|
||||
self._attrs["desc"] = (
|
||||
realtime.get("life_index", {})
|
||||
.get("ultraviolet", {})
|
||||
.get("desc", "")
|
||||
)
|
||||
elif self.kind == "comfort":
|
||||
self._attrs["desc"] = (
|
||||
realtime.get("life_index", {}).get("comfort", {}).get("desc", "")
|
||||
)
|
||||
elif self.kind == "precipitation":
|
||||
precipitation = realtime.get("precipitation", {})
|
||||
self._attrs["datasource"] = (
|
||||
precipitation.get("local", {}).get("datasource", "")
|
||||
)
|
||||
self._attrs["nearest_intensity"] = (
|
||||
precipitation.get("nearest", {}).get("intensity", "")
|
||||
)
|
||||
self._attrs["nearest_distance"] = (
|
||||
precipitation.get("nearest", {}).get("distance", "")
|
||||
)
|
||||
return self._attrs
|
||||
|
||||
@property
|
||||
def entity_registry_enabled_default(self):
|
||||
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||
return bool(self.kind not in OPTIONAL_SENSORS)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Connect to dispatcher listening for entity data notifications."""
|
||||
self.async_on_remove(
|
||||
self.coordinator.async_add_listener(self.async_write_ha_state)
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Update Colorfulclouds entity."""
|
||||
await self.coordinator.async_request_refresh()
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Colorful Clouds",
|
||||
"description": "If you need help with configuration, check here: https://github.com/fineemb/Colorfulclouds-weather",
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"api_version": "API version, the default is v2.5",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"name": "Name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect to the Colorfulclouds API.",
|
||||
"communication": "The key may be invalid, please check the input value."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "This location is already configured."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Colorful Clouds options",
|
||||
"description": "Set the number of days you need to get forecast data, as well as extreme weather warning data",
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"api_version": "API version, the default is v2.5",
|
||||
"dailysteps": "Day forecast (5-15)",
|
||||
"hourlysteps": "Hourly forecast (24-360)",
|
||||
"alert": "Extreme weather warnings",
|
||||
"starttime": "The forecast start time, -1 is yesterday, -2 is the day before yesterday",
|
||||
"interval": "Refresh interval in minutes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect to the Colorfulclouds API.",
|
||||
"communication": "The key may be invalid, please check the input value."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"config": {
|
||||
"title": "Colorful Clouds",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Colorful Clouds",
|
||||
"description": "If you need help with configuration, check here: https://github.com/fineemb/Colorfulclouds-weather",
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"api_version": "API version,The default is v2.5",
|
||||
"latitude" : "Latitude",
|
||||
"longitude" : "Longitude",
|
||||
"name" : "Name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect to the Colorfulclouds API.",
|
||||
"communication": "The key may be invalid, please check the input value."
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "Only a single configuration is allowed.",
|
||||
"already_configured": "Do not repeat the configuration."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Colorful Clouds options",
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"api_version": "API version, the default is v2.5",
|
||||
"dailysteps": "Day Forecast (5-15)",
|
||||
"hourlysteps": "Hourly forecast (24-360)",
|
||||
"alert": "Extreme weather warnings",
|
||||
"starttime": "The forecast start time, -1 is yesterday, -2 is the day before yesterday",
|
||||
"interval": "Refresh interval in minutes"
|
||||
},
|
||||
"description": "Set the number of days you need to get forecast data, as well as extreme weather warning data"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Cannot connect to the Colorfulclouds API.",
|
||||
"communication": "The key may be invalid, please check the input value."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"config": {
|
||||
"title": "彩云天气",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "彩云天气",
|
||||
"description": "如果您需要配置方面的帮助,请查看此处: https://github.com/fineemb/Colorfulclouds-weather",
|
||||
"data": {
|
||||
"api_key": "彩云API Key",
|
||||
"api_version": "API版本,默认是v2.5",
|
||||
"latitude" : "维度",
|
||||
"longitude" : "经度",
|
||||
"name" : "名称"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "无法连接到彩云天气 API。",
|
||||
"communication": "Key可能无效,请检查输入值."
|
||||
},
|
||||
"abort": {
|
||||
"single_instance_allowed": "仅允许单个配置.",
|
||||
"already_configured": "请勿重复配置."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "彩云天气选项",
|
||||
"data": {
|
||||
"api_key": "彩云API Key",
|
||||
"api_version": "API版本,默认v2.5",
|
||||
"dailysteps": "天级预报(5-15)",
|
||||
"hourlysteps": "小时级预报(24-360)",
|
||||
"alert": "极端天气预警",
|
||||
"starttime": "预报起始时间,-1 是昨天,-2 是前天",
|
||||
"interval": "定时刷新数据间隔(分钟)"
|
||||
},
|
||||
"description": "设置你需要获取预报数据的天数,以及极端天气预警数据"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "无法连接到彩云天气 API。",
|
||||
"communication": "Key可能无效,请检查输入值."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_HAIL,
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
ATTR_CONDITION_FOG,
|
||||
ATTR_CONDITION_LIGHTNING_RAINY,
|
||||
ATTR_CONDITION_PARTLYCLOUDY,
|
||||
ATTR_CONDITION_RAINY,
|
||||
ATTR_CONDITION_SNOWY,
|
||||
ATTR_CONDITION_SNOWY_RAINY,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_WINDY,
|
||||
ATTR_CONDITION_POURING,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION,
|
||||
ATTR_FORECAST_HUMIDITY,
|
||||
ATTR_WEATHER_CLOUD_COVERAGE,
|
||||
ATTR_WEATHER_HUMIDITY,
|
||||
ATTR_WEATHER_PRESSURE,
|
||||
ATTR_WEATHER_VISIBILITY,
|
||||
Forecast,
|
||||
WeatherEntity,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
|
||||
from .const import ATTRIBUTION, COORDINATOR, DOMAIN, MANUFACTURER
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONDITION_MAP = {
|
||||
"CLEAR_DAY": ATTR_CONDITION_SUNNY,
|
||||
"CLEAR_NIGHT": ATTR_CONDITION_CLEAR_NIGHT,
|
||||
"PARTLY_CLOUDY_DAY": ATTR_CONDITION_PARTLYCLOUDY,
|
||||
"PARTLY_CLOUDY_NIGHT": ATTR_CONDITION_PARTLYCLOUDY,
|
||||
"CLOUDY": ATTR_CONDITION_CLOUDY,
|
||||
"LIGHT_HAZE": ATTR_CONDITION_FOG,
|
||||
"MODERATE_HAZE": ATTR_CONDITION_FOG,
|
||||
"HEAVY_HAZE": ATTR_CONDITION_FOG,
|
||||
"LIGHT_RAIN": ATTR_CONDITION_RAINY,
|
||||
"MODERATE_RAIN": ATTR_CONDITION_RAINY,
|
||||
"HEAVY_RAIN": ATTR_CONDITION_POURING,
|
||||
"STORM_RAIN": ATTR_CONDITION_POURING,
|
||||
"FOG": ATTR_CONDITION_FOG,
|
||||
"LIGHT_SNOW": ATTR_CONDITION_SNOWY,
|
||||
"MODERATE_SNOW": ATTR_CONDITION_SNOWY,
|
||||
"HEAVY_SNOW": ATTR_CONDITION_SNOWY,
|
||||
"STORM_SNOW": ATTR_CONDITION_SNOWY,
|
||||
"DUST": ATTR_CONDITION_FOG,
|
||||
"SAND": ATTR_CONDITION_FOG,
|
||||
"THUNDER_SHOWER": ATTR_CONDITION_LIGHTNING_RAINY,
|
||||
"HAIL": ATTR_CONDITION_HAIL,
|
||||
"SLEET": ATTR_CONDITION_SNOWY_RAINY,
|
||||
"WIND": ATTR_CONDITION_WINDY,
|
||||
"HAZE": ATTR_CONDITION_FOG,
|
||||
"RAIN": ATTR_CONDITION_RAINY,
|
||||
"SNOW": ATTR_CONDITION_SNOWY,
|
||||
"FREEZE": ATTR_CONDITION_SNOWY_RAINY,
|
||||
"BLOWING_SAND": ATTR_CONDITION_WINDY,
|
||||
}
|
||||
|
||||
|
||||
def _get_condition(skycon: str, default: str | None = None) -> str | None:
|
||||
"""Map a Colorfulclouds skycon to a Home Assistant weather condition."""
|
||||
return CONDITION_MAP.get(skycon, default)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Add a Colorfulclouds weather entity from a config_entry."""
|
||||
name = config_entry.data[CONF_NAME]
|
||||
|
||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
|
||||
# _LOGGER.debug("metric: %s", coordinator.data["is_metric"])
|
||||
|
||||
async_add_entities([ColorfulCloudsEntity(name, coordinator)], True)
|
||||
|
||||
|
||||
class ColorfulCloudsEntity(WeatherEntity):
|
||||
"""Representation of a weather condition."""
|
||||
|
||||
_attr_supported_features = (
|
||||
WeatherEntityFeature.FORECAST_HOURLY | WeatherEntityFeature.FORECAST_DAILY
|
||||
)
|
||||
|
||||
def __init__(self, name, coordinator):
|
||||
self.coordinator = coordinator
|
||||
_LOGGER.debug("coordinator: %s", coordinator.data["server_time"])
|
||||
self._name = name
|
||||
self._attrs = {}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return ATTRIBUTION
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
_LOGGER.debug("weather_unique_id: %s", self.coordinator.data["location_key"])
|
||||
return self.coordinator.data["location_key"]
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.coordinator.data["location_key"])},
|
||||
"name": self._name,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"entry_type": DeviceEntryType.SERVICE,
|
||||
}
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling requirement of the entity."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
return self.coordinator.last_update_success
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the weather condition."""
|
||||
skycon = self.coordinator.data["result"]["realtime"]["skycon"]
|
||||
return _get_condition(skycon)
|
||||
|
||||
@property
|
||||
def cloud_coverage(self):
|
||||
return round(
|
||||
float(self.coordinator.data["result"]["realtime"]["cloudrate"]) * 100
|
||||
)
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
"""Return the weather condition."""
|
||||
skycon = self.coordinator.data["result"]["realtime"]["skycon"]
|
||||
return _get_condition(skycon)
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
return round(float(self.coordinator.data["result"]["realtime"]["humidity"]) * 100)
|
||||
|
||||
@property
|
||||
def native_apparent_temperature(self):
|
||||
return self.coordinator.data["result"]["realtime"]["apparent_temperature"]
|
||||
|
||||
@property
|
||||
def native_precipitation_unit(self):
|
||||
return "mm" if self.coordinator.data["is_metric"] == "metric:v2" else "in"
|
||||
|
||||
@property
|
||||
def native_pressure(self):
|
||||
return self.coordinator.data["result"]["realtime"]["pressure"]
|
||||
|
||||
@property
|
||||
def native_pressure_unit(self):
|
||||
return "Pa"
|
||||
|
||||
@property
|
||||
def native_temperature(self):
|
||||
return self.coordinator.data["result"]["realtime"]["temperature"]
|
||||
|
||||
@property
|
||||
def native_temperature_unit(self):
|
||||
return "°C" if self.coordinator.data["is_metric"] == "metric:v2" else "°F"
|
||||
|
||||
@property
|
||||
def native_visibility(self):
|
||||
"""能见度"""
|
||||
return self.coordinator.data["result"]["realtime"]["visibility"]
|
||||
|
||||
@property
|
||||
def native_visibility_unit(self):
|
||||
return "km" if self.coordinator.data["is_metric"] == "metric:v2" else "mi"
|
||||
|
||||
@property
|
||||
def native_wind_speed(self):
|
||||
"""风速"""
|
||||
return self.coordinator.data["result"]["realtime"]["wind"]["speed"]
|
||||
|
||||
@property
|
||||
def wind_bearing(self):
|
||||
"""风向"""
|
||||
return self.coordinator.data["result"]["realtime"]["wind"]["direction"]
|
||||
|
||||
@property
|
||||
def native_wind_speed_unit(self):
|
||||
return "km/h" if self.coordinator.data["is_metric"] == "metric:v2" else "mph"
|
||||
|
||||
@property
|
||||
def uv_index(self):
|
||||
"""紫外线"""
|
||||
return self.coordinator.data["result"]["realtime"]["life_index"]["ultraviolet"][
|
||||
"index"
|
||||
]
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
result = self.coordinator.data["result"]
|
||||
realtime = result["realtime"]
|
||||
minutely = result.get("minutely", {})
|
||||
air_quality = realtime.get("air_quality", {})
|
||||
aqi = air_quality.get("aqi", {})
|
||||
aqi_description = air_quality.get("description", {})
|
||||
|
||||
self._attrs = {
|
||||
"skycon": realtime["skycon"],
|
||||
"forecast_hourly": result.get("hourly", {}).get("description", ""),
|
||||
"forecast_minutely": minutely.get("description", ""),
|
||||
"forecast_probability": minutely.get("probability", ""),
|
||||
"forecast_keypoint": result.get("forecast_keypoint", ""),
|
||||
"forecast_alert": result.get("alert", ""),
|
||||
"pm25": air_quality.get("pm25", ""),
|
||||
"pm10": air_quality.get("pm10", ""),
|
||||
"o3": air_quality.get("o3", ""),
|
||||
"no2": air_quality.get("no2", ""),
|
||||
"so2": air_quality.get("so2", ""),
|
||||
"co": air_quality.get("co", ""),
|
||||
"aqi": aqi.get("chn", ""),
|
||||
"aqi_description": aqi_description.get("chn", ""),
|
||||
"aqi_usa": aqi.get("usa", ""),
|
||||
"aqi_usa_description": aqi_description.get("usa", ""),
|
||||
}
|
||||
return self._attrs
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Connect to dispatcher listening for entity data notifications."""
|
||||
await super().async_added_to_hass()
|
||||
self.async_on_remove(
|
||||
self.coordinator.async_add_listener(self.async_write_ha_state)
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Update Colorfulclouds entity."""
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
async def async_forecast_daily(self) -> list[Forecast] | None:
|
||||
daily = self.coordinator.data["result"]["daily"]
|
||||
forecast_data = []
|
||||
try:
|
||||
for i in range(len(daily["temperature"])):
|
||||
skycon = daily["skycon"][i]["value"]
|
||||
forecast_data.append(
|
||||
{
|
||||
ATTR_FORECAST_TIME: daily["temperature"][i]["date"],
|
||||
"skycon": skycon,
|
||||
ATTR_FORECAST_CONDITION: _get_condition(
|
||||
skycon, ATTR_CONDITION_CLOUDY
|
||||
),
|
||||
ATTR_FORECAST_NATIVE_TEMP: daily["temperature"][i]["max"],
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: daily["temperature"][i][
|
||||
"min"
|
||||
],
|
||||
ATTR_FORECAST_WIND_BEARING: daily["wind"][i]["avg"][
|
||||
"direction"
|
||||
],
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: daily["wind"][i]["avg"][
|
||||
"speed"
|
||||
],
|
||||
ATTR_FORECAST_PRECIPITATION: daily["precipitation"][i][
|
||||
"avg"
|
||||
],
|
||||
ATTR_FORECAST_HUMIDITY: round(
|
||||
float(daily["humidity"][i]["avg"]) * 100
|
||||
),
|
||||
ATTR_WEATHER_CLOUD_COVERAGE: round(
|
||||
float(daily["cloudrate"][i]["avg"]) * 100
|
||||
),
|
||||
ATTR_WEATHER_PRESSURE: daily["pressure"][i]["avg"],
|
||||
ATTR_WEATHER_VISIBILITY: daily["visibility"][i]["avg"],
|
||||
}
|
||||
)
|
||||
except (KeyError, IndexError, TypeError) as error:
|
||||
_LOGGER.warning("Unable to build daily forecast: %s", error)
|
||||
|
||||
return forecast_data
|
||||
|
||||
async def async_forecast_hourly(self) -> list[Forecast] | None:
|
||||
hourly = self.coordinator.data["result"]["hourly"]
|
||||
forecast_data = []
|
||||
try:
|
||||
for i in range(len(hourly["temperature"])):
|
||||
skycon = hourly["skycon"][i]["value"]
|
||||
forecast_data.append(
|
||||
{
|
||||
ATTR_FORECAST_TIME: hourly["temperature"][i]["datetime"],
|
||||
"skycon": skycon,
|
||||
ATTR_FORECAST_CONDITION: _get_condition(
|
||||
skycon, ATTR_CONDITION_CLOUDY
|
||||
),
|
||||
ATTR_FORECAST_NATIVE_TEMP: hourly["temperature"][i][
|
||||
"value"
|
||||
],
|
||||
ATTR_FORECAST_WIND_BEARING: hourly["wind"][i]["direction"],
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: hourly["wind"][i]["speed"],
|
||||
ATTR_FORECAST_PRECIPITATION: hourly["precipitation"][i][
|
||||
"value"
|
||||
],
|
||||
ATTR_WEATHER_HUMIDITY: round(
|
||||
float(hourly["humidity"][i]["value"]) * 100
|
||||
),
|
||||
ATTR_WEATHER_CLOUD_COVERAGE: round(
|
||||
float(hourly["cloudrate"][i]["value"]) * 100
|
||||
),
|
||||
ATTR_WEATHER_PRESSURE: hourly["pressure"][i]["value"],
|
||||
ATTR_WEATHER_VISIBILITY: hourly["visibility"][i]["value"],
|
||||
}
|
||||
)
|
||||
except (KeyError, IndexError, TypeError) as error:
|
||||
_LOGGER.warning("Unable to build hourly forecast: %s", error)
|
||||
|
||||
return forecast_data
|
||||
Reference in New Issue
Block a user