New apps Added
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
"""The Pirate Weather component."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LANGUAGE,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_MODE,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
CONF_NAME,
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import (
|
||||
CONF_ENDPOINT,
|
||||
CONF_MODELS,
|
||||
CONF_UNITS,
|
||||
DEFAULT_ENDPOINT,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOMAIN,
|
||||
ENTRY_NAME,
|
||||
ENTRY_WEATHER_COORDINATOR,
|
||||
PLATFORMS,
|
||||
PW_PLATFORM,
|
||||
PW_PLATFORMS,
|
||||
PW_ROUND,
|
||||
UPDATE_LISTENER,
|
||||
)
|
||||
|
||||
# from .weather_update_coordinator import WeatherUpdateCoordinator, DarkSkyData
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
|
||||
CONF_FORECAST = "forecast"
|
||||
CONF_HOURLY_FORECAST = "hourly_forecast"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
ATTRIBUTION = "Powered by Pirate Weather"
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Pirate Weather as config entry."""
|
||||
name = entry.data[CONF_NAME]
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
latitude = _get_config_value(entry, CONF_LATITUDE)
|
||||
longitude = _get_config_value(entry, CONF_LONGITUDE)
|
||||
forecast_mode = "daily"
|
||||
conditions = _get_config_value(entry, CONF_MONITORED_CONDITIONS)
|
||||
units = _get_config_value(entry, CONF_UNITS)
|
||||
forecast_days = _get_config_value(entry, CONF_FORECAST)
|
||||
forecast_hours = _get_config_value(entry, CONF_HOURLY_FORECAST)
|
||||
pw_entity_platform = _get_config_value(entry, PW_PLATFORM)
|
||||
pw_entity_rounding = _get_config_value(entry, PW_ROUND)
|
||||
scan_interval = _get_config_value(entry, CONF_SCAN_INTERVAL)
|
||||
language = _get_config_value(entry, CONF_LANGUAGE)
|
||||
endpoint = _get_config_value(entry, CONF_ENDPOINT)
|
||||
models = _get_config_value(entry, CONF_MODELS)
|
||||
|
||||
# If scan_interval config value is not configured fall back to the entry data config value
|
||||
if not scan_interval:
|
||||
scan_interval = entry.data.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
# If endpoint config value is not configured fall back to the default
|
||||
if not endpoint:
|
||||
endpoint = DEFAULT_ENDPOINT
|
||||
_LOGGER.info("Using default Pirate Weather Endpoint")
|
||||
|
||||
# If latitude or longitude is not configured fall back to the HA location
|
||||
if not latitude:
|
||||
latitude = hass.config.latitude
|
||||
if not longitude:
|
||||
longitude = hass.config.longitude
|
||||
|
||||
scan_interval = max(scan_interval, 60)
|
||||
|
||||
# Extract list of int from forecast days/ hours string if present
|
||||
# _LOGGER.warning('forecast_days_type: ' + str(type(forecast_days)))
|
||||
|
||||
# _LOGGER.warning(forecast_days)
|
||||
if isinstance(forecast_days, str):
|
||||
# If empty, set to none
|
||||
if forecast_days in {"", "None"}:
|
||||
forecast_days = None
|
||||
else:
|
||||
if forecast_days[0] == "[":
|
||||
forecast_days = forecast_days[1:-1].split(",")
|
||||
else:
|
||||
forecast_days = forecast_days.split(",")
|
||||
forecast_days = [int(i) for i in forecast_days]
|
||||
|
||||
if isinstance(forecast_hours, str):
|
||||
# If empty, set to none
|
||||
if forecast_hours in {"", "None"}:
|
||||
forecast_hours = None
|
||||
else:
|
||||
if forecast_hours[0] == "[":
|
||||
forecast_hours = forecast_hours[1:-1].split(",")
|
||||
else:
|
||||
forecast_hours = forecast_hours.split(",")
|
||||
forecast_hours = [int(i) for i in forecast_hours]
|
||||
|
||||
unique_location = f"pw-{latitude}-{longitude}"
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
# Create and link weather WeatherUpdateCoordinator
|
||||
weather_coordinator = WeatherUpdateCoordinator(
|
||||
api_key,
|
||||
latitude,
|
||||
longitude,
|
||||
timedelta(seconds=scan_interval),
|
||||
language,
|
||||
endpoint,
|
||||
units,
|
||||
hass,
|
||||
entry,
|
||||
models,
|
||||
)
|
||||
hass.data[DOMAIN][unique_location] = weather_coordinator
|
||||
|
||||
# await weather_coordinator.async_refresh()
|
||||
await weather_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = {
|
||||
ENTRY_NAME: name,
|
||||
ENTRY_WEATHER_COORDINATOR: weather_coordinator,
|
||||
CONF_API_KEY: api_key,
|
||||
CONF_LATITUDE: latitude,
|
||||
CONF_LONGITUDE: longitude,
|
||||
CONF_UNITS: units,
|
||||
CONF_MONITORED_CONDITIONS: conditions,
|
||||
CONF_MODE: forecast_mode,
|
||||
CONF_FORECAST: forecast_days,
|
||||
CONF_HOURLY_FORECAST: forecast_hours,
|
||||
PW_PLATFORM: pw_entity_platform,
|
||||
PW_ROUND: pw_entity_rounding,
|
||||
CONF_SCAN_INTERVAL: scan_interval,
|
||||
CONF_LANGUAGE: language,
|
||||
CONF_ENDPOINT: endpoint,
|
||||
CONF_MODELS: models,
|
||||
}
|
||||
|
||||
# Setup platforms
|
||||
# If both platforms
|
||||
if (PW_PLATFORMS[0] in pw_entity_platform) and (
|
||||
PW_PLATFORMS[1] in pw_entity_platform
|
||||
):
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
# If only sensor
|
||||
elif PW_PLATFORMS[0] in pw_entity_platform:
|
||||
await hass.config_entries.async_forward_entry_setups(entry, [PLATFORMS[0]])
|
||||
# If only weather
|
||||
elif PW_PLATFORMS[1] in pw_entity_platform:
|
||||
await hass.config_entries.async_forward_entry_setups(entry, [PLATFORMS[1]])
|
||||
|
||||
update_listener = entry.add_update_listener(async_update_options)
|
||||
hass.data[DOMAIN][entry.entry_id][UPDATE_LISTENER] = update_listener
|
||||
return True
|
||||
|
||||
|
||||
async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update options."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
pw_entity_prevplatform = hass.data[DOMAIN][entry.entry_id][PW_PLATFORM]
|
||||
|
||||
# If both
|
||||
if (PW_PLATFORMS[0] in pw_entity_prevplatform) and (
|
||||
PW_PLATFORMS[1] in pw_entity_prevplatform
|
||||
):
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
# If only sensor
|
||||
elif PW_PLATFORMS[0] in pw_entity_prevplatform:
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
entry, [PLATFORMS[0]]
|
||||
)
|
||||
# If only Weather
|
||||
elif PW_PLATFORMS[1] in pw_entity_prevplatform:
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
entry, [PLATFORMS[1]]
|
||||
)
|
||||
|
||||
_LOGGER.info("Unloading Pirate Weather")
|
||||
|
||||
if unload_ok:
|
||||
update_listener = hass.data[DOMAIN][entry.entry_id][UPDATE_LISTENER]
|
||||
update_listener()
|
||||
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
def _get_config_value(config_entry: ConfigEntry, key: str) -> Any:
|
||||
if config_entry.options and key in config_entry.options:
|
||||
return config_entry.options[key]
|
||||
# Check if key exists
|
||||
if config_entry.data and key in config_entry.data:
|
||||
return config_entry.data[key]
|
||||
return None
|
||||
|
||||
|
||||
def _filter_domain_configs(elements, domain):
|
||||
return list(filter(lambda elem: elem["platform"] == domain, elements))
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,335 @@
|
||||
"""Config flow for Pirate Weather."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import voluptuous as vol
|
||||
from aiohttp import ClientError
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_MODE,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
CONF_NAME,
|
||||
CONF_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
ALL_CONDITIONS,
|
||||
CONF_ENDPOINT,
|
||||
CONF_LANGUAGE,
|
||||
CONF_MODELS,
|
||||
CONF_UNITS,
|
||||
CONFIG_FLOW_VERSION,
|
||||
DEFAULT_ENDPOINT,
|
||||
DEFAULT_FORECAST_MODE,
|
||||
DEFAULT_LANGUAGE,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEFAULT_UNITS,
|
||||
DOMAIN,
|
||||
LANGUAGES,
|
||||
PW_PLATFORM,
|
||||
PW_PLATFORMS,
|
||||
PW_PREVPLATFORM,
|
||||
PW_ROUND,
|
||||
)
|
||||
|
||||
ATTRIBUTION = "Powered by Pirate Weather"
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_FORECAST = "forecast"
|
||||
CONF_HOURLY_FORECAST = "hourly_forecast"
|
||||
|
||||
|
||||
class PirateWeatherConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Config flow for PirateWeather."""
|
||||
|
||||
VERSION = CONFIG_FLOW_VERSION
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
) -> PirateWeatherOptionsFlow:
|
||||
"""Get the options flow for this handler."""
|
||||
return PirateWeatherOptionsFlow()
|
||||
|
||||
async def async_step_user(self, user_input=None) -> ConfigFlowResult:
|
||||
"""Handle a flow initialized by the user."""
|
||||
errors = {}
|
||||
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): str,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
|
||||
vol.Optional(
|
||||
CONF_LATITUDE, default=self.hass.config.latitude
|
||||
): cv.latitude,
|
||||
vol.Optional(
|
||||
CONF_LONGITUDE, default=self.hass.config.longitude
|
||||
): cv.longitude,
|
||||
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): int,
|
||||
vol.Required(PW_PLATFORM, default=[PW_PLATFORMS[1]]): cv.multi_select(
|
||||
PW_PLATFORMS
|
||||
),
|
||||
vol.Required(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(
|
||||
LANGUAGES
|
||||
),
|
||||
vol.Optional(CONF_MODELS, default=""): str,
|
||||
vol.Optional(CONF_FORECAST, default=""): str,
|
||||
vol.Optional(CONF_HOURLY_FORECAST, default=""): str,
|
||||
vol.Optional(CONF_MONITORED_CONDITIONS, default=[]): cv.multi_select(
|
||||
ALL_CONDITIONS
|
||||
),
|
||||
vol.Optional(PW_ROUND, default="No"): vol.In(["Yes", "No"]),
|
||||
vol.Optional(CONF_UNITS, default=DEFAULT_UNITS): vol.In(
|
||||
["si", "us", "ca", "uk"]
|
||||
),
|
||||
vol.Optional(CONF_ENDPOINT, default=DEFAULT_ENDPOINT): str,
|
||||
}
|
||||
)
|
||||
|
||||
if user_input is not None:
|
||||
latitude = user_input[CONF_LATITUDE]
|
||||
longitude = user_input[CONF_LONGITUDE]
|
||||
forecastMode = "daily"
|
||||
forecastPlatform = user_input[PW_PLATFORM]
|
||||
entityNamee = user_input[CONF_NAME]
|
||||
endpoint = user_input[CONF_ENDPOINT]
|
||||
|
||||
# Convert scan interval to timedelta
|
||||
if isinstance(user_input[CONF_SCAN_INTERVAL], str):
|
||||
user_input[CONF_SCAN_INTERVAL] = cv.time_period_str(
|
||||
user_input[CONF_SCAN_INTERVAL]
|
||||
)
|
||||
|
||||
# Convert scan interval to number of seconds
|
||||
if isinstance(user_input[CONF_SCAN_INTERVAL], timedelta):
|
||||
user_input[CONF_SCAN_INTERVAL] = user_input[
|
||||
CONF_SCAN_INTERVAL
|
||||
].total_seconds()
|
||||
|
||||
# Unique value includes the location and forcastHours/ forecastDays to seperate WeatherEntity/ Sensor
|
||||
# await self.async_set_unique_id(f"pw-{latitude}-{longitude}-{forecastDays}-{forecastHours}-{forecastMode}-{entityNamee}")
|
||||
await self.async_set_unique_id(
|
||||
f"pw-{latitude}-{longitude}-{forecastPlatform}-{forecastMode}-{entityNamee}"
|
||||
)
|
||||
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
try:
|
||||
api_status = await _is_pw_api_online(
|
||||
self.hass, user_input[CONF_API_KEY], latitude, longitude, endpoint
|
||||
)
|
||||
|
||||
if api_status == 403:
|
||||
_LOGGER.warning(
|
||||
"Pirate Weather Setup Error: Invalid API Key, Ensure that you've subscribed to API at https://pirate-weather.apiable.io/"
|
||||
)
|
||||
errors["base"] = (
|
||||
"Invalid API Key, Ensure that you've subscribed to API at https://pirate-weather.apiable.io/"
|
||||
)
|
||||
|
||||
except ClientError:
|
||||
_LOGGER.warning(
|
||||
"Pirate Weather Setup Error: API HTTP Error: %s", api_status
|
||||
)
|
||||
errors["base"] = "API Error: %s", api_status
|
||||
|
||||
if errors:
|
||||
_LOGGER.warning(errors)
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_NAME], data=user_input
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||
|
||||
async def async_step_import(self, import_input=None):
|
||||
"""Set the config entry up from yaml."""
|
||||
config = import_input.copy()
|
||||
|
||||
if CONF_NAME not in config:
|
||||
config[CONF_NAME] = DEFAULT_NAME
|
||||
if CONF_LATITUDE not in config:
|
||||
config[CONF_LATITUDE] = self.hass.config.latitude
|
||||
if CONF_LONGITUDE not in config:
|
||||
config[CONF_LONGITUDE] = self.hass.config.longitude
|
||||
if CONF_MODE not in config:
|
||||
config[CONF_MODE] = DEFAULT_FORECAST_MODE
|
||||
if CONF_LANGUAGE not in config:
|
||||
config[CONF_LANGUAGE] = DEFAULT_LANGUAGE
|
||||
if CONF_UNITS not in config:
|
||||
config[CONF_UNITS] = DEFAULT_UNITS
|
||||
if CONF_MONITORED_CONDITIONS not in config:
|
||||
config[CONF_MONITORED_CONDITIONS] = []
|
||||
if CONF_FORECAST not in config:
|
||||
config[CONF_FORECAST] = ""
|
||||
if CONF_HOURLY_FORECAST not in config:
|
||||
config[CONF_HOURLY_FORECAST] = ""
|
||||
if CONF_API_KEY not in config:
|
||||
config[CONF_API_KEY] = None
|
||||
if PW_PLATFORM not in config:
|
||||
config[PW_PLATFORM] = None
|
||||
if PW_PREVPLATFORM not in config:
|
||||
config[PW_PREVPLATFORM] = None
|
||||
if PW_ROUND not in config:
|
||||
config[PW_ROUND] = "No"
|
||||
if CONF_SCAN_INTERVAL not in config:
|
||||
config[CONF_SCAN_INTERVAL] = DEFAULT_SCAN_INTERVAL
|
||||
if CONF_ENDPOINT not in config:
|
||||
config[CONF_ENDPOINT] = DEFAULT_ENDPOINT
|
||||
return await self.async_step_user(config)
|
||||
|
||||
|
||||
class PirateWeatherOptionsFlow(OptionsFlow):
|
||||
"""Handle options."""
|
||||
|
||||
async def async_step_init(self, user_input: dict | None = None) -> ConfigFlowResult:
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
# if self.config_entry.options:
|
||||
# user_input[PW_PREVPLATFORM] = self.config_entry.options[PW_PLATFORM]
|
||||
# else:
|
||||
# user_input[PW_PREVPLATFORM] = self.hass.data[DOMAIN][entry.entry_id][PW_PLATFORM]
|
||||
# self.hass.data[DOMAIN][entry.entry_id][PW_PREVPLATFORM] = self.hass.data[DOMAIN][entry.entry_id][PW_PLATFORM]
|
||||
# user_input[PW_PREVPLATFORM] = self.hass.data[DOMAIN][entry.entry_id][PW_PLATFORM]
|
||||
# _LOGGER.warning('async_step_init_Options')
|
||||
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=self._get_options_schema(),
|
||||
)
|
||||
|
||||
def _get_options_schema(self):
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_NAME,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_NAME,
|
||||
self.config_entry.data.get(CONF_NAME, DEFAULT_NAME),
|
||||
),
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_LATITUDE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_LATITUDE,
|
||||
self.config_entry.data.get(
|
||||
CONF_LATITUDE, self.hass.config.latitude
|
||||
),
|
||||
),
|
||||
): cv.latitude,
|
||||
vol.Optional(
|
||||
CONF_LONGITUDE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_LONGITUDE,
|
||||
self.config_entry.data.get(
|
||||
CONF_LONGITUDE, self.hass.config.longitude
|
||||
),
|
||||
),
|
||||
): cv.longitude,
|
||||
vol.Optional(
|
||||
CONF_SCAN_INTERVAL,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_SCAN_INTERVAL,
|
||||
self.config_entry.data.get(
|
||||
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
|
||||
),
|
||||
),
|
||||
): int,
|
||||
vol.Required(
|
||||
PW_PLATFORM,
|
||||
default=self.config_entry.options.get(
|
||||
PW_PLATFORM,
|
||||
self.config_entry.data.get(PW_PLATFORM, []),
|
||||
),
|
||||
): cv.multi_select(PW_PLATFORMS),
|
||||
vol.Optional(
|
||||
CONF_LANGUAGE,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_LANGUAGE,
|
||||
self.config_entry.data.get(CONF_LANGUAGE, DEFAULT_LANGUAGE),
|
||||
),
|
||||
): vol.In(LANGUAGES),
|
||||
vol.Optional(
|
||||
CONF_MODELS,
|
||||
default=str(
|
||||
self.config_entry.options.get(
|
||||
CONF_MODELS,
|
||||
self.config_entry.data.get(CONF_MODELS, ""),
|
||||
),
|
||||
),
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_FORECAST,
|
||||
default=str(
|
||||
self.config_entry.options.get(
|
||||
CONF_FORECAST,
|
||||
self.config_entry.data.get(CONF_FORECAST, ""),
|
||||
),
|
||||
),
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_HOURLY_FORECAST,
|
||||
default=str(
|
||||
self.config_entry.options.get(
|
||||
CONF_HOURLY_FORECAST,
|
||||
self.config_entry.data.get(CONF_HOURLY_FORECAST, ""),
|
||||
),
|
||||
),
|
||||
): str,
|
||||
vol.Optional(
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
self.config_entry.data.get(CONF_MONITORED_CONDITIONS, []),
|
||||
),
|
||||
): cv.multi_select(ALL_CONDITIONS),
|
||||
vol.Optional(
|
||||
CONF_UNITS,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_UNITS,
|
||||
self.config_entry.data.get(CONF_UNITS, DEFAULT_UNITS),
|
||||
),
|
||||
): vol.In(["si", "us", "ca", "uk"]),
|
||||
vol.Optional(
|
||||
PW_ROUND,
|
||||
default=self.config_entry.options.get(
|
||||
PW_ROUND,
|
||||
self.config_entry.options.get(PW_ROUND, "No"),
|
||||
),
|
||||
): vol.In(["Yes", "No"]),
|
||||
vol.Optional(
|
||||
CONF_ENDPOINT,
|
||||
default=str(
|
||||
self.config_entry.options.get(
|
||||
CONF_ENDPOINT,
|
||||
self.config_entry.data.get(CONF_ENDPOINT, DEFAULT_ENDPOINT),
|
||||
),
|
||||
),
|
||||
): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def _is_pw_api_online(hass, api_key, lat, lon, endpoint):
|
||||
forecastString = endpoint + "/forecast/" + api_key + "/" + str(lat) + "," + str(lon)
|
||||
|
||||
session = async_get_clientsession(hass)
|
||||
async with session.get(forecastString) as resp:
|
||||
return resp.status
|
||||
@@ -0,0 +1,358 @@
|
||||
"""Consts for the Pirate Weather."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_PRECIPITATION,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_PRESSURE,
|
||||
ATTR_FORECAST_TEMP,
|
||||
ATTR_FORECAST_TEMP_LOW,
|
||||
ATTR_FORECAST_TIME,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
DEGREE,
|
||||
PERCENTAGE,
|
||||
UV_INDEX,
|
||||
Platform,
|
||||
UnitOfLength,
|
||||
UnitOfPressure,
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
|
||||
DOMAIN = "pirateweather"
|
||||
DEFAULT_NAME = "PirateWeather"
|
||||
DEFAULT_LANGUAGE = "en"
|
||||
DEFAULT_UNITS = "us"
|
||||
DEFAULT_SCAN_INTERVAL = 1200
|
||||
DEFAULT_ENDPOINT = "https://api.pirateweather.net"
|
||||
ATTRIBUTION = "Data provided by Pirate Weather GUI"
|
||||
MANUFACTURER = "PirateWeather"
|
||||
CONF_LANGUAGE = "language"
|
||||
CONF_UNITS = "units"
|
||||
CONF_ENDPOINT = "endpoint"
|
||||
CONF_MODELS = "models"
|
||||
CONFIG_FLOW_VERSION = 2
|
||||
ENTRY_NAME = "name"
|
||||
ENTRY_WEATHER_COORDINATOR = "weather_coordinator"
|
||||
ATTR_API_PRECIPITATION = "precipitation"
|
||||
ATTR_API_PRECIPITATION_KIND = "precipitation_kind"
|
||||
ATTR_API_DATETIME = "datetime"
|
||||
ATTR_API_DEW_POINT = "dew_point"
|
||||
ATTR_API_WEATHER = "weather"
|
||||
ATTR_API_TEMPERATURE = "temperature"
|
||||
ATTR_API_FEELS_LIKE_TEMPERATURE = "feels_like_temperature"
|
||||
ATTR_API_WIND_SPEED = "wind_speed"
|
||||
ATTR_API_WIND_BEARING = "wind_bearing"
|
||||
ATTR_API_HUMIDITY = "humidity"
|
||||
ATTR_API_PRESSURE = "pressure"
|
||||
ATTR_API_CONDITION = "condition"
|
||||
ATTR_API_CLOUDS = "clouds"
|
||||
ATTR_API_RAIN = "rain"
|
||||
ATTR_API_SNOW = "snow"
|
||||
ATTR_API_UV_INDEX = "uv_index"
|
||||
ATTR_API_WEATHER_CODE = "weather_code"
|
||||
ATTR_API_FORECAST = "forecast"
|
||||
UPDATE_LISTENER = "update_listener"
|
||||
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
||||
PW_PLATFORMS = ["Sensor", "Weather"]
|
||||
PW_PLATFORM = "pw_platform"
|
||||
PW_PREVPLATFORM = "pw_prevplatform"
|
||||
PW_ROUND = "pw_round"
|
||||
|
||||
ATTR_FORECAST_CLOUD_COVERAGE = "cloud_coverage"
|
||||
ATTR_FORECAST_HUMIDITY = "humidity"
|
||||
ATTR_FORECAST_NATIVE_VISIBILITY = "native_visibility"
|
||||
|
||||
FORECAST_MODE_HOURLY = "hourly"
|
||||
FORECAST_MODE_DAILY = "daily"
|
||||
|
||||
FORECAST_MODES = [
|
||||
FORECAST_MODE_HOURLY,
|
||||
FORECAST_MODE_DAILY,
|
||||
]
|
||||
|
||||
|
||||
DEFAULT_FORECAST_MODE = FORECAST_MODE_DAILY
|
||||
|
||||
FORECASTS_HOURLY = "forecasts_hourly"
|
||||
FORECASTS_DAILY = "forecasts_daily"
|
||||
|
||||
ALL_CONDITIONS = {
|
||||
"summary": "Summary",
|
||||
"icon": "Icon",
|
||||
"precip_type": "Precipitation Type",
|
||||
"precip_intensity": "Precipitation Intensity",
|
||||
"precip_probability": "Precipitation Probability",
|
||||
"precip_accumulation": "Precipitation Accumulation",
|
||||
"temperature": "Temperature",
|
||||
"apparent_temperature": "Apparent Temperature",
|
||||
"dew_point": "Dew Point",
|
||||
"humidity": "Humidity",
|
||||
"wind_speed": "Wind Speed",
|
||||
"wind_gust": "Wind Gust",
|
||||
"wind_bearing": "Wind Bearing",
|
||||
"cloud_cover": "Cloud Cover",
|
||||
"pressure": "Pressure",
|
||||
"visibility": "Visibility",
|
||||
"ozone": "Ozone",
|
||||
"minutely_summary": "Minutely Summary",
|
||||
"hourly_summary": "Hourly Summary",
|
||||
"daily_summary": "Daily Summary",
|
||||
"temperature_high": "Temperature High",
|
||||
"temperature_low": "Temperature Low",
|
||||
"apparent_temperature_high": "Apparent Temperature High",
|
||||
"apparent_temperature_low": "Apparent Temperature Low",
|
||||
"precip_intensity_max": "Precip Intensity Max",
|
||||
"uv_index": "UV Index",
|
||||
"moon_phase": "Moon Phase",
|
||||
"sunrise_time": "Sunrise Time",
|
||||
"sunset_time": "Sunset Time",
|
||||
"nearest_storm_distance": "Nearest Storm Distance",
|
||||
"nearest_storm_bearing": "Nearest Storm Bearing",
|
||||
"alerts": "Alerts",
|
||||
"time": "Time",
|
||||
"fire_index": "Fire Index",
|
||||
"fire_index_max": "Fire Index Max",
|
||||
"fire_risk_level": "Fire Risk Level",
|
||||
"smoke": "Smoke",
|
||||
"smoke_max": "Smoke Max",
|
||||
"liquid_accumulation": "Liquid Accumulation",
|
||||
"snow_accumulation": "Snow Accumulation",
|
||||
"ice_accumulation": "Ice Accumulation",
|
||||
"apparent_temperature_high_time": "Daytime High Apparent Temperature Time",
|
||||
"apparent_temperature_low_time": "Overnight Low Apparent Temperature Time",
|
||||
"temperature_high_time": "Daytime High Temperature Time",
|
||||
"temperature_min_time": "Low Temperature Time",
|
||||
"hrrr_subh_update_time": "HRRR SubH Update Time",
|
||||
"hrrr_0_18_update_time": "HRRR 0-18 Update Time",
|
||||
"nbm_update_time": "NBM Update Time",
|
||||
"nbm_fire_update_time": "NBM Fire Update Time",
|
||||
"hrrr_18_48_update_time": "HRRR 18-48 Update Time",
|
||||
"gfs_update_time": "GFS Update Time",
|
||||
"gefs_update_time": "GEFS Update Time",
|
||||
"current_day_liquid": "Current Day Liquid Accumulation",
|
||||
"current_day_snow": "Current Day Snow Accumulation",
|
||||
"current_day_ice": "Current Day Ice Accumulation",
|
||||
"cape": "Convective Available Potential Energy",
|
||||
"cape_max": "Convective Available Potential Energy Max",
|
||||
"solar": "Downward Short-Wave Radiation Flux",
|
||||
"solar_max": "Downward Short-Wave Radiation Flux Max",
|
||||
"rain_intensity": "Rain Intensity",
|
||||
"rain_intensity_max": "Rain Intensity Max",
|
||||
"snow_intensity": "Snow Intensity",
|
||||
"snow_intensity_max": "Snow Intensity Max",
|
||||
"ice_intensity": "Ice Intensity",
|
||||
"ice_intensity_max": "Ice Intensity Max",
|
||||
}
|
||||
|
||||
LANGUAGES = [
|
||||
"ar",
|
||||
"az",
|
||||
"be",
|
||||
"bg",
|
||||
"bn",
|
||||
"bs",
|
||||
"ca",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"el",
|
||||
"en",
|
||||
"eo",
|
||||
"es",
|
||||
"et",
|
||||
"fa",
|
||||
"fi",
|
||||
"fr",
|
||||
"ga",
|
||||
"gd",
|
||||
"he",
|
||||
"hi",
|
||||
"hr",
|
||||
"hu",
|
||||
"id",
|
||||
"is",
|
||||
"it",
|
||||
"ja",
|
||||
"ka",
|
||||
"kn",
|
||||
"ko",
|
||||
"kw",
|
||||
"lv",
|
||||
"ml",
|
||||
"mr",
|
||||
"nl",
|
||||
"no",
|
||||
"pa",
|
||||
"pl",
|
||||
"pt",
|
||||
"ro",
|
||||
"ru",
|
||||
"sk",
|
||||
"sl",
|
||||
"sr",
|
||||
"sv",
|
||||
"ta",
|
||||
"te",
|
||||
"tet",
|
||||
"tr",
|
||||
"uk",
|
||||
"ur",
|
||||
"vi",
|
||||
"x-pig-latin",
|
||||
"zh",
|
||||
"zh-tw",
|
||||
]
|
||||
|
||||
WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WEATHER,
|
||||
name="Weather",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_DEW_POINT,
|
||||
name="Dew Point",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_FEELS_LIKE_TEMPERATURE,
|
||||
name="Feels like temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_HUMIDITY,
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_CLOUDS,
|
||||
name="Cloud coverage",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_RAIN,
|
||||
name="Rain",
|
||||
native_unit_of_measurement=UnitOfLength.MILLIMETERS,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_SNOW,
|
||||
name="Snow",
|
||||
native_unit_of_measurement=UnitOfLength.MILLIMETERS,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_PRECIPITATION_KIND,
|
||||
name="Precipitation kind",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_UV_INDEX,
|
||||
name="UV Index",
|
||||
native_unit_of_measurement=UV_INDEX,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_CONDITION,
|
||||
name="Condition",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WEATHER_CODE,
|
||||
name="Weather Code",
|
||||
),
|
||||
)
|
||||
FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_CONDITION,
|
||||
name="Condition",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION,
|
||||
name="Precipitation",
|
||||
native_unit_of_measurement=UnitOfLength.MILLIMETERS,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
name="Precipitation probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TEMP,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TEMP_LOW,
|
||||
name="Temperature Low",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TIME,
|
||||
name="Time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_CLOUDS,
|
||||
name="Cloud coverage",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,188 @@
|
||||
"""Taken from the fantastic Dark Sky library: https://github.com/ZeevG/python-forecast.io in October 2024. Updated in April 2025 using the Pirate Weather library: https://github.com/cloneofghosts/python-pirate-weather."""
|
||||
|
||||
import datetime
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class UnicodeMixin:
|
||||
"""Provide string representation for Python 2/3 compatibility."""
|
||||
|
||||
def __str__(self):
|
||||
"""Return the unicode representation of the object for Python 2/3 compatibility."""
|
||||
return self.__unicode__()
|
||||
|
||||
|
||||
"""Models used in the Pirate Weather library."""
|
||||
|
||||
|
||||
class Forecast(UnicodeMixin):
|
||||
"""Represent the forecast data and provide methods to access weather blocks."""
|
||||
|
||||
def __init__(self, data, response, headers):
|
||||
"""Initialize the Forecast with data, HTTP response, and headers."""
|
||||
self.response = response
|
||||
self.http_headers = headers
|
||||
self.json = data
|
||||
|
||||
self._alerts = []
|
||||
for alertJSON in self.json.get("alerts", []):
|
||||
self._alerts.append(Alert(alertJSON))
|
||||
|
||||
def update(self):
|
||||
"""Update the forecast data by making a new request to the same URL."""
|
||||
r = requests.get(self.response.url)
|
||||
self.json = r.json()
|
||||
self.response = r
|
||||
|
||||
def currently(self):
|
||||
"""Return the current weather data block."""
|
||||
return self._pirateweather_data("currently")
|
||||
|
||||
def minutely(self):
|
||||
"""Return the minutely weather data block."""
|
||||
return self._pirateweather_data("minutely")
|
||||
|
||||
def hourly(self):
|
||||
"""Return the hourly weather data block."""
|
||||
return self._pirateweather_data("hourly")
|
||||
|
||||
def day_night(self):
|
||||
"""Return the day_night weather data block."""
|
||||
return self._pirateweather_data("day_night")
|
||||
|
||||
def daily(self):
|
||||
"""Return the daily weather data block."""
|
||||
return self._pirateweather_data("daily")
|
||||
|
||||
def flags(self):
|
||||
"""Return the flags data block."""
|
||||
return self._pirateweather_data("flags")
|
||||
|
||||
def offset(self):
|
||||
"""Return the time zone offset for the forecast location."""
|
||||
return self.json["offset"]
|
||||
|
||||
def alerts(self):
|
||||
"""Return the list of alerts issued for this forecast."""
|
||||
return self._alerts
|
||||
|
||||
def _pirateweather_data(self, key):
|
||||
"""Fetch and return specific weather data (currently, minutely, hourly, daily, flags and day_night)."""
|
||||
keys = ["minutely", "currently", "hourly", "daily", "flags", "day_night"]
|
||||
try:
|
||||
if key not in self.json:
|
||||
keys.remove(key)
|
||||
url = "{}&exclude={}{}".format(
|
||||
self.response.url.split("&")[0],
|
||||
",".join(keys),
|
||||
",alerts",
|
||||
)
|
||||
|
||||
response = requests.get(url).json()
|
||||
self.json[key] = response[key]
|
||||
|
||||
if key == "currently":
|
||||
return PirateWeatherDataPoint(self.json[key])
|
||||
if key == "flags":
|
||||
return PirateWeatherFlagsBlock(self.json[key])
|
||||
return PirateWeatherDataBlock(self.json[key])
|
||||
except KeyError:
|
||||
if key == "currently":
|
||||
return PirateWeatherDataPoint()
|
||||
return PirateWeatherDataBlock()
|
||||
|
||||
|
||||
class PirateWeatherDataBlock(UnicodeMixin):
|
||||
"""Represent a block of weather data such as minutely, hourly, or daily summaries."""
|
||||
|
||||
def __init__(self, d=None):
|
||||
"""Initialize the data block with summary and icon information."""
|
||||
d = d or {}
|
||||
self.summary = d.get("summary")
|
||||
self.icon = d.get("icon")
|
||||
self.data = [
|
||||
PirateWeatherDataPoint(datapoint) for datapoint in d.get("data", [])
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
"""Return a string representation of the data block."""
|
||||
return f"<PirateWeatherDataBlock instance: {self.summary} with {len(self.data)} PirateWeatherDataPoints>"
|
||||
|
||||
|
||||
class PirateWeatherFlagsBlock(UnicodeMixin):
|
||||
"""Represent a block of flags data."""
|
||||
|
||||
def __init__(self, d=None):
|
||||
"""Initialize the data block with flags information."""
|
||||
d = d or {}
|
||||
self.units = d.get("units")
|
||||
self.version = d.get("version")
|
||||
self.nearestStation = d.get("nearest-station")
|
||||
self.sources = list(d.get("sources"))
|
||||
self.sourceTimes = d.get("sourceTimes")
|
||||
self.processTime = d.get("processTime")
|
||||
self.ingestVersion = d.get("ingestVersion")
|
||||
self.nearestCity = d.get("nearestCity")
|
||||
self.nearestCountry = d.get("nearestCountry")
|
||||
self.nearestSubNational = d.get("nearestSubNational")
|
||||
|
||||
def __unicode__(self):
|
||||
"""Return a string representation of the data block."""
|
||||
return f"<PirateWeatherFlagsDataBlock instance: {self.version}>"
|
||||
|
||||
|
||||
class PirateWeatherDataPoint(UnicodeMixin):
|
||||
"""Represent a single data point in a weather forecast, such as an hourly or daily data point."""
|
||||
|
||||
def __init__(self, d={}):
|
||||
"""Initialize the data point with timestamp and weather information."""
|
||||
self.d = d
|
||||
|
||||
try:
|
||||
self.time = datetime.datetime.fromtimestamp(int(d["time"]))
|
||||
self.utime = d["time"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
try:
|
||||
sr_time = int(d["sunriseTime"])
|
||||
self.sunriseTime = datetime.datetime.fromtimestamp(sr_time)
|
||||
except KeyError:
|
||||
self.sunriseTime = None
|
||||
|
||||
try:
|
||||
ss_time = int(d["sunsetTime"])
|
||||
self.sunsetTime = datetime.datetime.fromtimestamp(ss_time)
|
||||
except KeyError:
|
||||
self.sunsetTime = None
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Return the weather property dynamically or return None if missing."""
|
||||
try:
|
||||
return self.d[name]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __unicode__(self):
|
||||
"""Return a string representation of the data point."""
|
||||
return f"<PirateWeatherDataPoint instance: {self.summary} at {self.time}>"
|
||||
|
||||
|
||||
class Alert(UnicodeMixin):
|
||||
"""Represent a weather alert, such as a storm warning or flood alert."""
|
||||
|
||||
def __init__(self, json):
|
||||
"""Initialize the alert with the raw JSON data."""
|
||||
self.json = json
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Return the alert property dynamically or return None if missing."""
|
||||
try:
|
||||
return self.json[name]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __unicode__(self):
|
||||
"""Return a string representation of the alert."""
|
||||
return f"<Alert instance: {self.title} at {self.time}>"
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"domain": "pirateweather",
|
||||
"name": "Pirate Weather",
|
||||
"codeowners": [
|
||||
"@alexander0042"
|
||||
],
|
||||
"config_flow": true,
|
||||
"dependencies": [],
|
||||
"documentation": "https://github.com/alexander0042/pirate-weather-ha",
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"issue_tracker": "https://github.com/alexander0042/pirate-weather-ha/issues",
|
||||
"version": "1.8.9"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Místo je již nakonfigurováno"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nepodařilo se připojit",
|
||||
"invalid_api_key": "Neplatný API klíč"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API klíč",
|
||||
"language": "Jazyk",
|
||||
"latitude": "Zeměpisná šířka",
|
||||
"longitude": "Zeměpisná délka",
|
||||
"mode": "Režim předpovědi pro entitu Počasí",
|
||||
"name": "Název integrace",
|
||||
"units": "Jednotky.",
|
||||
"models": "Modely počasí k vyloučení (csv)",
|
||||
"forecast": "Denní předpovědi senzorů ve formátu csv od 0 do 7 (např. '0,1,3'). Používá se pouze pokud jsou požadovány senzory.",
|
||||
"hourly_forecast": "Hodinové předpovědi senzorů ve formátu csv od 0 do 168 (např. '0,1,10'). Používá se pouze pokud jsou požadovány senzory.",
|
||||
"monitored_conditions": "Monitorované podmínky pro vytváření senzorů. Používá se pouze pokud jsou požadovány senzory.",
|
||||
"pw_platform": "Entita počasí a/nebo entita senzoru. Senzor vytvoří entity pro každou podmínku v každém čase. Pokud si nejste jisti, vyberte pouze Počasí!",
|
||||
"pw_round": "Zaokrouhlit hodnoty na nejbližší celé číslo. Ujistěte se, že vybrané jednotky odpovídají systémovým jednotkám.",
|
||||
"scan_interval": "Sekundy čekání mezi aktualizacemi. Snížení této hodnoty pod 900 sekund (15 minut) se nedoporučuje.",
|
||||
"endpoint": "Koncový bod pro použití vývojářského nebo lokálního zdroje s https://. Výchozí nastavení je api.pirateweather.net"
|
||||
},
|
||||
"description": "Nastavte integraci Pirate Weather. Pro generování API klíče jděte na pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "např. '0,1,4' (bez uvozovek)",
|
||||
"hourly_forecast": "např. '0,1,4' (bez uvozovek)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "API klíč",
|
||||
"language": "Jazyk",
|
||||
"latitude": "Zeměpisná šířka",
|
||||
"longitude": "Zeměpisná délka",
|
||||
"mode": "Režim předpovědi pro Entitu Počasí",
|
||||
"name": "Název integrace",
|
||||
"units": "Jednotky.",
|
||||
"models": "Modely počasí k vyloučení (csv)",
|
||||
"forecast": "Denní předpovědi senzorů ve formátu csv od 0 do 7 (např. '0,1,3'). Používá se pouze pokud jsou požadovány senzory.\n POZNÁMKA: Odebrání senzorů způsobí, že vzniknou nedostupné entity, které je potřeba smazat.",
|
||||
"hourly_forecast": "Hodinové předpovědi senzorů ve formátu csv od 0 do 168 (např. '0,1,10'). Používá se pouze pokud jsou požadovány senzory.\n POZNÁMKA: Odebrání senzorů způsobí, že vzniknou nedostupné entity, které je potřeba smazat.",
|
||||
"monitored_conditions": "Monitorované podmínky pro vytváření senzorů. Používá se pouze pokud jsou požadovány senzory.\n POZNÁMKA: Odebrání senzorů způsobí, že vzniknou nedostupné entity, které je potřeba smazat.",
|
||||
"pw_platform": "Entita Počasí a/nebo Entita Senzoru. Senzor vytvoří entity pro každou podmínku v každém čase. Pokud si nejste jisti, vyberte pouze Počasí!",
|
||||
"pw_round": "Zaokrouhlit hodnoty na nejbližší celé číslo. Ujistěte se, že vybrané jednotky odpovídají systémovým jednotkám.",
|
||||
"endpoint": "Koncový bod pro použití vývojářského nebo lokálního zdroje s https://. Výchozí nastavení je api.pirateweather.net"
|
||||
},
|
||||
"description": "Nastavte integraci Pirate Weather. Pro vygenerování API klíče jděte na pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "např. '0,1,4'.\n Pro odebrání existujících senzorů napište 'None' (bez uvozovek).",
|
||||
"hourly_forecast": "např. '0,1,4'.\n Pro odebrání existujících senzorů napište 'None' (bez uvozovek)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Standort ist bereits konfiguriert"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Fehler beim Verbinden",
|
||||
"invalid_api_key": "Ungültiger API-Schlüssel"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API-Schlüssel",
|
||||
"language": "Sprache",
|
||||
"latitude": "Breitengrad. Auf 0 setzen um aktuelle Position zu verwenden",
|
||||
"longitude": "Längengerad. Auf 0 setzen um aktuelle Position zu verwenden",
|
||||
"mode": "Vorhersage-Modus für die Wetter-Entität",
|
||||
"name": "Name der Integration",
|
||||
"units": "Einheiten.",
|
||||
"models": "Wettermodelle zum Ausschließen (csv)",
|
||||
"forecast": "Tägliche Vorhersage-Sensoren im csv-Format von 0-7 (z. B. '0,1,3'). Wird nur verwendet, wenn Sensoren angefordert werden.",
|
||||
"hourly_forecast": "Stündliche Vorhersage-Sensoren im csv-Format von 0-168 (z. B. '0,1,10'). Nur verwendet, wenn Sensoren angefordert werden.",
|
||||
"monitored_conditions": "Überwachte Bedingungen, für die Sensoren erstellt werden sollen. Wird nur verwendet, wenn Sensoren angefordert werden.",
|
||||
"pw_platform": "Wetter- und/oder Sensor-Entität. Sensor wird für jede Bedingung zu jeder Zeit Entitäten erstellen. Wenn Sie unsicher sind, wählen Sie nur Wetter!",
|
||||
"pw_round": "Rundet die Werte auf die nächstliegende Ganzzahl. Stellen Sie sicher, dass die gewählten Einheiten mit den Systemeinheiten übereinstimmen.",
|
||||
"scan_interval": "Sekunden, die zwischen den Aktualisierungen gewartet werden. Es wird nicht empfohlen, diesen Wert unter 900 Sekunden (15 Minuten) zu senken.",
|
||||
"endpoint": "Endpunkt zur Verwendung der Entwicklungs- oder lokalen Quelle mit https://. Standard ist api.pirateweather.net"
|
||||
},
|
||||
"description": "Pirate Weather-Integration einrichten. Besuche pirateweather.net um einen API-Schlüssel zu generieren.",
|
||||
"data_description": {
|
||||
"forecast": "z.B. '0,1,4' (ohne Anführungszeichen)",
|
||||
"hourly_forecast": "z.B. '0,1,4' (ohne Anführungszeichen)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "API-Schlüssel",
|
||||
"language": "Sprache",
|
||||
"latitude": "Breitengrad. Auf 0 setzen um aktuelle Position zu verwenden",
|
||||
"longitude": "Längengerad. Auf 0 setzen um aktuelle Position zu verwenden",
|
||||
"scan_interval": "Sekunden, die zwischen den Aktualisierungen gewartet werden. Es wird nicht empfohlen, diesen Wert unter 900 Sekunden (15 Minuten) zu senken.",
|
||||
"mode": "Vorhersage-Modus für die Wetter-Entität",
|
||||
"name": "Name der Integration",
|
||||
"units": "Einheiten.",
|
||||
"models": "Wettermodelle zum Ausschließen (csv)",
|
||||
"forecast": "Tägliche Vorhersage-Sensoren im csv-Format von 0-7 (z. B. '0,1,3'). Wird nur verwendet, wenn Sensoren angefordert werden.\n HINWEIS: Das Entfernen von Sensoren führt zu verwaisten Einheiten, die gelöscht werden müssen.",
|
||||
"hourly_forecast": "Stündliche Vorhersage-Sensoren im csv-Format von 0-168 (z. B. '0,1,10'). Nur verwendet, wenn Sensoren angefordert werden.\n HINWEIS: Das Entfernen von Sensoren führt zu verwaisten Einheiten, die gelöscht werden müssen.",
|
||||
"monitored_conditions": "Überwachte Bedingungen, für die Sensoren erstellt werden sollen. Wird nur verwendet, wenn Sensoren angefordert werden.",
|
||||
"pw_platform": "Wetter- und/oder Sensor-Entität. Sensor wird für jede Bedingung zu jeder Zeit Entitäten erstellen. Wenn Sie unsicher sind, wählen Sie nur Wetter!",
|
||||
"pw_round": "Rundet die Werte auf die nächstliegende Ganzzahl. Stellen Sie sicher, dass die gewählten Einheiten mit den Systemeinheiten übereinstimmen.",
|
||||
"endpoint": "Endpunkt zur Verwendung der Entwicklungs- oder lokalen Quelle mit https://. Standard ist api.pirateweather.net"
|
||||
},
|
||||
"description": "Pirate Weather-Integration einrichten. Besuche pirateweather.net um einen API-Schlüssel zu generieren.",
|
||||
"data_description": {
|
||||
"forecast": "z.B '0,1,4'.\n Gib 'None' (ohne Anführungszeichen) ein, um bestehende Sensoren zu entfernen.",
|
||||
"hourly_forecast": "z.B. '0,1,4'.\n Gib 'None' (ohne Anführungszeichen) ein, um bestehende Sensoren zu entfernen."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Location is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_api_key": "Invalid API key"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"language": "Language",
|
||||
"latitude": "Latitude. Set as 0 to use current location",
|
||||
"longitude": "Longitude. Set as 0 to use current location",
|
||||
"mode": "Forecast mode for the Weather entity",
|
||||
"name": "Integration Name",
|
||||
"units": "Units.",
|
||||
"models": "Weather models to exclude (csv)",
|
||||
"forecast": "Daily forecasts sensors in csv form from 0-7 (ex. '0,1,3'). Only used if sensors are requested.",
|
||||
"hourly_forecast": "Hourly forecast sensors in csv form from 0-168 (ex. '0,1,10'). Only used if sensors are requested.",
|
||||
"monitored_conditions": "Monitored conditions to create sensors for. Only used if sensors are requested.",
|
||||
"pw_platform": "Weather Entity and/or Sensor Entity. Sensor will create entities for each condition at each time. If unsure, only select Weather!",
|
||||
"pw_round": "Round values to the nearest integer. Ensure that the selected units match the system units.",
|
||||
"scan_interval": "Seconds to wait between updates. Reducing this below 900 seconds (15 minutes) is not recomended.",
|
||||
"endpoint": "Endpoint to use dev or local source, with https://. Default is api.pirateweather.net"
|
||||
},
|
||||
"description": "Set up Pirate Weather integration. To generate API key visit pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "ex. '0,1,4' (without quotes)",
|
||||
"hourly_forecast": "ex. '0,1,4' (without quotes)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"language": "Language",
|
||||
"latitude": "Latitude. Set as 0 to use current location",
|
||||
"longitude": "Longitude. Set as 0 to use current location",
|
||||
"scan_interval": "Seconds to wait between updates. Reducing this below 900 seconds (15 minutes) is not recomended.",
|
||||
"mode": "Forecast mode for the Weather entity",
|
||||
"name": "Integration Name",
|
||||
"units": "Units.",
|
||||
"models": "Weather models to exclude (csv)",
|
||||
"forecast": "Daily forecasts sensors in csv form from 0-7 (ex. '0,1,3'). Only used if sensors are requested.\n NOTE: Removing sensors will produce orphaned entities that need to be deleted.",
|
||||
"hourly_forecast": "Hourly forecast sensors in csv form from 0-168 (ex. '0,1,10'). Only used if sensors are requested.\n NOTE: Removing sensors will produce orphaned entities that need to be deleted.",
|
||||
"monitored_conditions": "Monitored conditions to create sensors for. Only used if sensors are requested.\n NOTE: Removing sensors will produce orphaned entities that need to be deleted.",
|
||||
"pw_platform": "Weather Entity and/or Sensor Entity. Sensor will create entities for each condition at each time. If unsure, only select Weather!",
|
||||
"pw_round": "Round values to the nearest integer. Ensure that the selected units match the system units.",
|
||||
"endpoint": "Endpoint to use dev or local source, with https://. Default is api.pirateweather.net"
|
||||
},
|
||||
"description": "Set up Pirate Weather integration. To generate API key visit pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "ex. '0,1,4'.\n To remove existing sensors, type 'None' (without quotes).",
|
||||
"hourly_forecast": "ex. '0,1,4'.\n To remove existing sensors, type 'None' (without quotes)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "La localisation est déjà configurée"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Échec de la connexion",
|
||||
"invalid_api_key": "Clé API invalide"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Clé API",
|
||||
"language": "Langue",
|
||||
"latitude": "Latitude. Définir à 0 pour utiliser la localisation actuelle",
|
||||
"longitude": "Longitude. Définir à 0 pour utiliser la localisation actuelle",
|
||||
"mode": "Mode de prévision pour l'entité Météo",
|
||||
"name": "Nom de l'intégration",
|
||||
"units": "Unités.",
|
||||
"models": "Modèles météo à exclure (CSV)",
|
||||
"forecast": "Capteurs de prévisions quotidiennes au format CSV de 0 à 7 (ex. '0,1,3'). Uniquement utilisé si les capteurs sont demandés.",
|
||||
"hourly_forecast": "Capteurs de prévisions horaires au format CSV de 0 à 168 (ex. '0,1,10'). Uniquement utilisé si les capteurs sont demandés.",
|
||||
"monitored_conditions": "Conditions surveillées pour créer des capteurs. Uniquement utilisé si les capteurs sont demandés.",
|
||||
"pw_platform": "Entité Météo et/ou Entité Capteur. Le capteur créera des entités pour chaque condition à chaque heure. Si vous n'êtes pas sûr, sélectionnez uniquement Météo !",
|
||||
"pw_round": "Arrondir les valeurs à l'entier le plus proche. Assurez-vous que les unités sélectionnées correspondent aux unités du système.",
|
||||
"scan_interval": "Secondes à attendre entre les mises à jour. Réduire cela en dessous de 900 secondes (15 minutes) n'est pas recommandé.",
|
||||
"endpoint": "Point de terminaison à utiliser pour la source dev ou locale, avec https://. La valeur par défaut est api.pirateweather.net"
|
||||
},
|
||||
"description": "Configurer l'intégration Pirate Weather. Pour générer une clé API, visitez pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "ex. '0,1,4' (sans guillemets)",
|
||||
"hourly_forecast": "ex. '0,1,4' (sans guillemets)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "Clé API",
|
||||
"language": "Langue",
|
||||
"latitude": "Latitude. Définir à 0 pour utiliser la localisation actuelle",
|
||||
"longitude": "Longitude. Définir à 0 pour utiliser la localisation actuelle",
|
||||
"scan_interval": "Secondes à attendre entre les mises à jour. Réduire cela en dessous de 900 secondes (15 minutes) n'est pas recommandé.",
|
||||
"mode": "Mode de prévision pour l'entité Météo",
|
||||
"name": "Nom de l'intégration",
|
||||
"units": "Unités.",
|
||||
"models": "Modèles météo à exclure (CSV)",
|
||||
"forecast": "Capteurs de prévisions quotidiennes au format CSV de 0 à 7 (ex. '0,1,3'). Uniquement utilisé si les capteurs sont demandés.\n NOTE: La suppression de capteurs produira des entités orphelines qui devront être supprimées.",
|
||||
"hourly_forecast": "Capteurs de prévisions horaires au format CSV de 0 à 168 (ex. '0,1,10'). Uniquement utilisé si les capteurs sont demandés.\n NOTE: La suppression de capteurs produira des entités orphelines qui devront être supprimées.",
|
||||
"monitored_conditions": "Conditions surveillées pour créer des capteurs. Uniquement utilisé si les capteurs sont demandés.\n NOTE: La suppression de capteurs produira des entités orphelines qui devront être supprimées.",
|
||||
"pw_platform": "Entité Météo et/ou Entité Capteur. Le capteur créera des entités pour chaque condition à chaque heure. Si vous n'êtes pas sûr, sélectionnez uniquement Météo !",
|
||||
"pw_round": "Arrondir les valeurs à l'entier le plus proche. Assurez-vous que les unités sélectionnées correspondent aux unités du système.",
|
||||
"endpoint": "Point de terminaison à utiliser pour la source dev ou locale, avec https://. La valeur par défaut est api.pirateweather.net"
|
||||
},
|
||||
"description": "Configurer l'intégration Pirate Weather. Pour générer une clé API, visitez pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "ex. '0,1,4'.\n Pour supprimer les capteurs existants, tapez 'None' (sans guillemets).",
|
||||
"hourly_forecast": "ex. '0,1,4'.\n Pour supprimer les capteurs existants, tapez 'None' (sans guillemets)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Locatie is al ingesteld"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Verbinding Mislukt",
|
||||
"invalid_api_key": "Foutieve API-sleutel"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API-sleutel",
|
||||
"language": "Taal",
|
||||
"latitude": "Breedtegraad",
|
||||
"longitude": "Lengtegraad",
|
||||
"mode": "Voorspellingsmodus voor de Weer entiteit",
|
||||
"name": "Integratie Naam",
|
||||
"units": "Eenheden.",
|
||||
"models": "Weermodellen om uit te sluiten (csv)",
|
||||
"forecast": "Dagelijkse prognoses sensoren in csv-vorm van 0-7 (bijv. '0,1,3'). Wordt alleen gebruikt als sensoren worden aangeroepen.",
|
||||
"hourly_forecast": "Uurlijkse prognoses sensoren in csv-vorm van 0-168 (bijv. '0,1,10'). Wordt alleen gebruikt als sensoren worden aangeroepen.",
|
||||
"monitored_conditions": "Gecontroleerde omstandigheden om sensoren voor te creëren. Wordt alleen gebruikt als sensoren worden aangeroepen.",
|
||||
"pw_platform": "Weer Entiteit en/of Sensor Entiteit. Sensor maakt op elk moment entiteiten voor elke voorwaarde. Als u het niet zeker weet, selecteert u alleen Weer!",
|
||||
"pw_round": "Rond waarden af op het dichtstbijzijnde gehele getal. Zorg ervoor dat de geselecteerde eenheden overeenkomen met de systeemeenheden.",
|
||||
"scan_interval": "Seconden wachten tussen updates. Het wordt niet aanbevolen om dit onder de 900 seconden (15 minuten) te zetten." ,
|
||||
"endpoint": "Eindpunt voor gebruik van dev of lokale bron, met https://. Standaard is dit api.pirateweather.net"
|
||||
},
|
||||
"description": "Stel Pirate Weather-integratie in. Ga naar pirateweather.net om een API-sleutel te genereren.",
|
||||
"data_description": {
|
||||
"forecast": "bijv. '0,1,4' (zonder aanhalingstekens)",
|
||||
"hourly_forecast": "bijv. '0,1,4' (zonder aanhalingstekens)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "API-sleutel",
|
||||
"language": "Taal",
|
||||
"latitude": "Breedtegraad",
|
||||
"longitude": "Lengtegraad",
|
||||
"mode": "Voorspellingsmodus voor de Weer entiteit",
|
||||
"name": "Integratie Naam",
|
||||
"units": "Eenheden.",
|
||||
"models": "Weermodellen om uit te sluiten (csv)",
|
||||
"forecast": "Dagelijkse prognoses sensoren in csv-vorm van 0-7 (bijv. '0,1,3'). Wordt alleen gebruikt als sensoren worden aangeroepen.",
|
||||
"hourly_forecast": "Uurlijkse prognoses sensoren in csv-vorm van 0-168 (bijv. '0,1,10'). Wordt alleen gebruikt als sensoren worden aangeroepen.",
|
||||
"monitored_conditions": "Gecontroleerde omstandigheden om sensoren voor te creëren. Wordt alleen gebruikt als sensoren worden aangeroepen.",
|
||||
"pw_platform": "Weer Entiteit en/of Sensor Entiteit. Sensor maakt op elk moment entiteiten voor elke voorwaarde. Als u het niet zeker weet, selecteert u alleen Weer!",
|
||||
"pw_round": "Rond waarden af op het dichtstbijzijnde gehele getal. Zorg ervoor dat de geselecteerde eenheden overeenkomen met de systeemeenheden.",
|
||||
"scan_interval": "Seconden wachten tussen updates. Het wordt niet aanbevolen om dit onder de 900 seconden (15 minuten) te zetten.",
|
||||
"endpoint": "Eindpunt voor gebruik van dev of lokale bron, met https://. Standaard is dit api.pirateweather.net"
|
||||
},
|
||||
"description": "Stel Pirate Weather-integratie in. Ga naar pirateweather.net om een API-sleutel te genereren.",
|
||||
"data_description": {
|
||||
"forecast": "bijv. '0,1,4' (zonder aanhalingstekens)",
|
||||
"hourly_forecast": "bijv. '0,1,4.\n om bestaande sensoren te verwijderen, type 'None' (zonder aanhalingstekens)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Lokalizacja jest już skonfigurowana"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nie udało się połączyć",
|
||||
"invalid_api_key": "Nieprawidłowy klucz API"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "Klucz API",
|
||||
"language": "Język",
|
||||
"latitude": "Szerokość geograficzna",
|
||||
"longitude": "Długość geograficzna",
|
||||
"mode": "Tryb prognozy dla encji Pogoda",
|
||||
"name": "Nazwa integracji",
|
||||
"units": "Jednostki.",
|
||||
"models": "Modele pogodowe do wykluczenia (csv)",
|
||||
"forecast": "Czujniki prognozy dziennej w postaci csv od 0 do 7 (np. '0,1,3'). Używane tylko wtedy, gdy wymagane są sensory.",
|
||||
"hourly_forecast": "Sensory prognozy godzinowej w formie csv od 0 do 168 (np. '0,1,10'). Używane tylko wtedy, gdy wymagane są sensory.",
|
||||
"monitored_conditions": "Monitorowane warunki do tworzenia czujników. Używane tylko wtedy, gdy wymagane są sensory.",
|
||||
"pw_platform": "Encja Pogody i/lub Encja Sensora. Sensor będzie tworzył encjei dla każdego warunku w każdym czasie. Jeśli nie jesteś pewien, wybierz tylko Pogoda!",
|
||||
"pw_round": "Zaokrąglij wartości do najbliższej liczby całkowitej. Upewnij się, że wybrane jednostki pasują do jednostek systemowych.",
|
||||
"scan_interval": "Sekundy oczekiwania między aktualizacjami. Nie zaleca się skracania tego poniżej 900 sekund (15 minut).",
|
||||
"endpoint": "Punkt końcowy do użycia dev lub lokalnego źródła, z https://. Domyślnie jest to api.pirateweather.net"
|
||||
},
|
||||
"description": "Skonfiguruj integrację Pirate Weather. Aby wygenerować klucz API, przejdź do pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "np. '0,1,4' (bez cudzysłowów)",
|
||||
"hourly_forecast": "np. '0,1,4' (bez cudzysłowów)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "Klucz API",
|
||||
"language": "Język",
|
||||
"latitude": "Szerokość geograficzna",
|
||||
"longitude": "Długość geograficzna",
|
||||
"mode": "Tryb prognozy dla encji Pogoda",
|
||||
"name": "Nazwa integracji",
|
||||
"units": "Jednostki.",
|
||||
"models": "Modele pogodowe do wykluczenia (csv)",
|
||||
"forecast": "Czujniki prognozy dziennej w postaci csv od 0 do 7 (np. '0,1,3'). Używane tylko wtedy, gdy wymagane są sensory. UWAGA: Usunięcie czujników spowoduje utworzenie osieroconych encji, które należy usunąć.",
|
||||
"hourly_forecast": "Sensory prognozy godzinowej w formie csv od 0 do 168 (np. '0,1,10'). Używane tylko wtedy, gdy wymagane są sensory. UWAGA: Usunięcie czujników spowoduje utworzenie osieroconych encji, które należy usunąć.",
|
||||
"monitored_conditions": "Monitorowane warunki do tworzenia czujników. Używane tylko wtedy, gdy wymagane są sensory. UWAGA: Usunięcie czujników spowoduje utworzenie osieroconych encji, które należy usunąć.",
|
||||
"pw_platform": "Encja Pogody i/lub Encja Sensora. Sensor będzie tworzył encjei dla każdego warunku w każdym czasie. Jeśli nie jesteś pewien, wybierz tylko Pogoda!",
|
||||
"pw_round": "Zaokrąglij wartości do najbliższej liczby całkowitej. Upewnij się, że wybrane jednostki pasują do jednostek systemowych.",
|
||||
"endpoint": "Punkt końcowy do użycia dev lub lokalnego źródła, z https://. Domyślnie jest to api.pirateweather.net"
|
||||
},
|
||||
"description": "Skonfiguruj integrację Pirate Weather. Aby wygenerować klucz API, przejdź do pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "np. '0,1,4'",
|
||||
"hourly_forecast": "np. '0,1,4'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Miesto je už nakonfigurované"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nepodarilo sa pripojiť",
|
||||
"invalid_api_key": "Neplatný API kľúč"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API kľúč",
|
||||
"language": "Jazyk",
|
||||
"latitude": "Zemepisná šírka",
|
||||
"longitude": "Zemepisná dĺžka",
|
||||
"mode": "Režim predpovede pre entitu Počasie",
|
||||
"name": "Názov integrácie",
|
||||
"units": "Jednotky.",
|
||||
"models": "Modely počasia na vylúčenie (csv)",
|
||||
"forecast": "Denné predpovede senzorov vo formáte csv od 0 do 7 (napr. '0,1,3'). Používa sa iba ak sú požadované senzory.",
|
||||
"hourly_forecast": "Hodinové predpovede senzorov vo formáte csv od 0 do 168 (napr. '0,1,10'). Používa sa iba ak sú požadované senzory.",
|
||||
"monitored_conditions": "Monitorované podmienky pre vytváranie senzorov. Používa sa iba ak sú požadované senzory.",
|
||||
"pw_platform": "Entita počasia a/alebo entita senzoru. Senzor vytvorí entity pre každú podmienku v každom čase. Ak si nie ste istí, vyberte iba Počasie!",
|
||||
"pw_round": "Zaokrúhliť hodnoty na najbližšie celé číslo. Uistite sa, že vybrané jednotky zodpovedajú systémovým jednotkám.",
|
||||
"scan_interval": "Sekundy čakania medzi aktualizáciami. Zníženie tejto hodnoty pod 900 sekúnd (15 minút) sa neodporúča.",
|
||||
"endpoint": "Koncový bod na použitie vývojového alebo lokálneho zdroja s https://. Predvolená hodnota je api.pirateweather.net"
|
||||
},
|
||||
"description": "Nastavte integráciu Pirate Weather. Pre generovanie API kľúča choďte na pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "napr. '0,1,4' (bez úvodzoviek)",
|
||||
"hourly_forecast": "napr. '0,1,4' (bez úvodzoviek)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"api_key": "API kľúč",
|
||||
"language": "Jazyk",
|
||||
"latitude": "Zemepisná šírka",
|
||||
"longitude": "Zemepisná dĺžka",
|
||||
"mode": "Režim predpovede pre Entitu Počasie",
|
||||
"name": "Názov integrácie",
|
||||
"units": "Jednotky.",
|
||||
"models": "Modely počasia na vylúčenie (csv)",
|
||||
"forecast": "Denné predpovede senzorov vo formáte csv od 0 do 7 (napr. '0,1,3'). Používa sa iba ak sú požadované senzory.\n POZNÁMKA: Odstránenie senzorov spôsobí, že vzniknú nedostupné entity, ktoré je potrebné vymazať.",
|
||||
"hourly_forecast": "Hodinové predpovede senzorov vo formáte csv od 0 do 168 (napr. '0,1,10'). Používa sa iba ak sú požadované senzory.\n POZNÁMKA: Odstránenie senzorov spôsobí, že vzniknú nedostupné entity, ktoré je potrebné vymazať.",
|
||||
"monitored_conditions": "Monitorované podmienky pre vytváranie senzorov. Používa sa iba ak sú požadované senzory.\n POZNÁMKA: Odstránenie senzorov spôsobí, že vzniknú nedostupné entity, ktoré je potrebné vymazať.",
|
||||
"pw_platform": "Entita Počasie a/alebo Entita Senzoru. Senzor vytvorí entity pre každú podmienku v každom čase. Ak si nie ste istí, vyberte iba Počasie!",
|
||||
"pw_round": "Zaokrúhliť hodnoty na najbližšie celé číslo. Uistite sa, že vybrané jednotky zodpovedajú systémovým jednotkám.",
|
||||
"endpoint": "Koncový bod na použitie vývojového alebo lokálneho zdroja s https://. Predvolená hodnota je api.pirateweather.net"
|
||||
},
|
||||
"description": "Nastavte integráciu Pirate Weather. Pre vygenerovanie API kľúča choďte na pirateweather.net",
|
||||
"data_description": {
|
||||
"forecast": "napr. '0,1,4'.\n Pre odstránenie existujúcich senzorov napíšte 'None' (bez úvodzoviek).",
|
||||
"hourly_forecast": "napr. '0,1,4'.\n Pre odstránenie existujúcich senzorov napíšte 'None' (bez úvodzoviek)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
"""Support for the Pirate Weather service."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import voluptuous as vol
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
ATTR_CONDITION_EXCEPTIONAL,
|
||||
ATTR_CONDITION_FOG,
|
||||
ATTR_CONDITION_HAIL,
|
||||
ATTR_CONDITION_LIGHTNING,
|
||||
ATTR_CONDITION_PARTLYCLOUDY,
|
||||
ATTR_CONDITION_RAINY,
|
||||
ATTR_CONDITION_SNOWY,
|
||||
ATTR_CONDITION_SNOWY_RAINY,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_CONDITION_WINDY,
|
||||
PLATFORM_SCHEMA,
|
||||
Forecast,
|
||||
SingleCoordinatorWeatherEntity,
|
||||
WeatherEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_MODE,
|
||||
CONF_NAME,
|
||||
CONF_SCAN_INTERVAL,
|
||||
UnitOfLength,
|
||||
UnitOfPrecipitationDepth,
|
||||
UnitOfPressure,
|
||||
UnitOfSpeed,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
|
||||
from .const import (
|
||||
CONF_UNITS,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOMAIN,
|
||||
ENTRY_WEATHER_COORDINATOR,
|
||||
FORECAST_MODES,
|
||||
PW_PLATFORM,
|
||||
PW_PLATFORMS,
|
||||
PW_PREVPLATFORM,
|
||||
PW_ROUND,
|
||||
)
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTRIBUTION = "Powered by Pirate Weather"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_LATITUDE): cv.latitude,
|
||||
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
||||
vol.Optional(PW_PLATFORM): cv.string,
|
||||
vol.Optional(PW_PREVPLATFORM): cv.string,
|
||||
vol.Optional(CONF_MODE, default="hourly"): vol.In(FORECAST_MODES),
|
||||
vol.Optional(CONF_UNITS): vol.In(["si", "us", "ca", "uk", "uk2"]),
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): cv.time_period,
|
||||
}
|
||||
)
|
||||
|
||||
MAP_CONDITION = {
|
||||
"clear-day": ATTR_CONDITION_SUNNY,
|
||||
"clear-night": ATTR_CONDITION_CLEAR_NIGHT,
|
||||
"rain": ATTR_CONDITION_RAINY,
|
||||
"snow": ATTR_CONDITION_SNOWY,
|
||||
"sleet": ATTR_CONDITION_SNOWY_RAINY,
|
||||
"wind": ATTR_CONDITION_WINDY,
|
||||
"fog": ATTR_CONDITION_FOG,
|
||||
"cloudy": ATTR_CONDITION_CLOUDY,
|
||||
"partly-cloudy-day": ATTR_CONDITION_PARTLYCLOUDY,
|
||||
"partly-cloudy-night": ATTR_CONDITION_PARTLYCLOUDY,
|
||||
"hail": ATTR_CONDITION_HAIL,
|
||||
"thunderstorm": ATTR_CONDITION_LIGHTNING,
|
||||
"tornado": ATTR_CONDITION_EXCEPTIONAL,
|
||||
"none": ATTR_CONDITION_EXCEPTIONAL,
|
||||
"mixed": ATTR_CONDITION_SNOWY_RAINY,
|
||||
}
|
||||
|
||||
WEATHER_UNITS = {
|
||||
"si": {
|
||||
"temperature": UnitOfTemperature.CELSIUS,
|
||||
"wind_speed": UnitOfSpeed.METERS_PER_SECOND,
|
||||
"pressure": UnitOfPressure.MBAR,
|
||||
"precipitation": UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
"visibility": UnitOfLength.KILOMETERS,
|
||||
},
|
||||
"us": {
|
||||
"temperature": UnitOfTemperature.FAHRENHEIT,
|
||||
"wind_speed": UnitOfSpeed.MILES_PER_HOUR,
|
||||
"pressure": UnitOfPressure.MBAR,
|
||||
"precipitation": UnitOfPrecipitationDepth.INCHES,
|
||||
"visibility": UnitOfLength.MILES,
|
||||
},
|
||||
"ca": {
|
||||
"temperature": UnitOfTemperature.CELSIUS,
|
||||
"wind_speed": UnitOfSpeed.KILOMETERS_PER_HOUR,
|
||||
"pressure": UnitOfPressure.MBAR,
|
||||
"precipitation": UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
"visibility": UnitOfLength.KILOMETERS,
|
||||
},
|
||||
"uk": {
|
||||
"temperature": UnitOfTemperature.CELSIUS,
|
||||
"wind_speed": UnitOfSpeed.MILES_PER_HOUR,
|
||||
"pressure": UnitOfPressure.MBAR,
|
||||
"precipitation": UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
"visibility": UnitOfLength.MILES,
|
||||
},
|
||||
"uk2": {
|
||||
"temperature": UnitOfTemperature.CELSIUS,
|
||||
"wind_speed": UnitOfSpeed.MILES_PER_HOUR,
|
||||
"pressure": UnitOfPressure.MBAR,
|
||||
"precipitation": UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
"visibility": UnitOfLength.MILES,
|
||||
},
|
||||
}
|
||||
|
||||
CONF_UNITS = "units"
|
||||
|
||||
DEFAULT_NAME = "Pirate Weather"
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Import the platform into a config entry."""
|
||||
_LOGGER.warning(
|
||||
"Configuration of Pirate Weather (weather entity) in YAML is deprecated "
|
||||
"Your existing configuration has been imported into the UI automatically "
|
||||
"and can be safely removed from your configuration.yaml file"
|
||||
)
|
||||
|
||||
# Add source to config
|
||||
config_entry[PW_PLATFORM] = [PW_PLATFORMS[1]]
|
||||
|
||||
# Set as no rounding for compatability
|
||||
config_entry[PW_ROUND] = "No"
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=config_entry
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _get_precip(forecast, unit_system: str) -> float | None:
|
||||
"""Get and convert precipitation accumulation."""
|
||||
precip = forecast.d.get("precipAccumulation")
|
||||
if precip is not None and unit_system != "us":
|
||||
return precip * 10
|
||||
return precip
|
||||
|
||||
|
||||
def _map_daily_forecast(forecast, unit_system) -> Forecast:
|
||||
precip = _get_precip(forecast, unit_system)
|
||||
return {
|
||||
"datetime": utc_from_timestamp(forecast.d.get("time")).isoformat(),
|
||||
"condition": MAP_CONDITION.get(forecast.d.get("icon")),
|
||||
"native_temperature": forecast.d.get("temperatureHigh"),
|
||||
"native_templow": forecast.d.get("temperatureLow"),
|
||||
"native_precipitation": precip,
|
||||
"precipitation_probability": round(
|
||||
forecast.d.get("precipProbability") * 100, 0
|
||||
),
|
||||
"humidity": round(forecast.d.get("humidity") * 100, 2),
|
||||
"cloud_coverage": round(forecast.d.get("cloudCover") * 100, 0),
|
||||
"native_wind_speed": round(forecast.d.get("windSpeed"), 2),
|
||||
"native_wind_gust_speed": round(forecast.d.get("windGust"), 2),
|
||||
"wind_bearing": round(forecast.d.get("windBearing"), 0),
|
||||
"native_dew_point": forecast.d.get("dewPoint"),
|
||||
"native_pressure": forecast.d.get("pressure"),
|
||||
"uv_index": round(forecast.d.get("uvIndex"), 2),
|
||||
}
|
||||
|
||||
|
||||
def _map_day_night_forecast(
|
||||
forecast, unit_system, is_day: bool | None = None
|
||||
) -> Forecast:
|
||||
precip = _get_precip(forecast, unit_system)
|
||||
# If caller provided an `is_day` hint (we'll pass parity from the list),
|
||||
# prefer that. Otherwise fall back to a minimal inference from the icon.
|
||||
is_daytime: bool | None = is_day
|
||||
if is_daytime is None:
|
||||
icon = forecast.d.get("icon")
|
||||
if isinstance(icon, str):
|
||||
if "night" in icon:
|
||||
is_daytime = False
|
||||
elif "day" in icon:
|
||||
is_daytime = True
|
||||
|
||||
return {
|
||||
"is_daytime": is_daytime,
|
||||
"datetime": utc_from_timestamp(forecast.d.get("time")).isoformat(),
|
||||
"condition": MAP_CONDITION.get(forecast.d.get("icon")),
|
||||
"native_temperature": forecast.d.get("temperature"),
|
||||
"native_apparent_temperature": forecast.d.get("apparentTemperature"),
|
||||
"native_dew_point": forecast.d.get("dewPoint"),
|
||||
"native_pressure": forecast.d.get("pressure"),
|
||||
"native_wind_speed": round(forecast.d.get("windSpeed"), 2),
|
||||
"wind_bearing": round(forecast.d.get("windBearing"), 0),
|
||||
"native_wind_gust_speed": round(forecast.d.get("windGust"), 2),
|
||||
"humidity": round(forecast.d.get("humidity") * 100, 2),
|
||||
"native_precipitation": precip,
|
||||
"precipitation_probability": round(
|
||||
forecast.d.get("precipProbability") * 100, 0
|
||||
),
|
||||
"cloud_coverage": round(forecast.d.get("cloudCover") * 100, 0),
|
||||
"uv_index": round(forecast.d.get("uvIndex"), 2),
|
||||
}
|
||||
|
||||
|
||||
def _map_hourly_forecast(forecast, unit_system) -> Forecast:
|
||||
precip = _get_precip(forecast, unit_system)
|
||||
return {
|
||||
"datetime": utc_from_timestamp(forecast.d.get("time")).isoformat(),
|
||||
"condition": MAP_CONDITION.get(forecast.d.get("icon")),
|
||||
"native_temperature": forecast.d.get("temperature"),
|
||||
"native_apparent_temperature": forecast.d.get("apparentTemperature"),
|
||||
"native_dew_point": forecast.d.get("dewPoint"),
|
||||
"native_pressure": forecast.d.get("pressure"),
|
||||
"native_wind_speed": round(forecast.d.get("windSpeed"), 2),
|
||||
"wind_bearing": round(forecast.d.get("windBearing"), 0),
|
||||
"native_wind_gust_speed": round(forecast.d.get("windGust"), 2),
|
||||
"humidity": round(forecast.d.get("humidity") * 100, 2),
|
||||
"native_precipitation": precip,
|
||||
"precipitation_probability": round(
|
||||
forecast.d.get("precipProbability") * 100, 0
|
||||
),
|
||||
"cloud_coverage": round(forecast.d.get("cloudCover") * 100, 0),
|
||||
"uv_index": round(forecast.d.get("uvIndex"), 2),
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Pirate Weather entity based on a config entry."""
|
||||
domain_data = hass.data[DOMAIN][config_entry.entry_id]
|
||||
name = domain_data[CONF_NAME]
|
||||
weather_coordinator = domain_data[ENTRY_WEATHER_COORDINATOR]
|
||||
forecast_mode = domain_data[CONF_MODE]
|
||||
|
||||
unique_id = f"{config_entry.unique_id}"
|
||||
|
||||
# Round Output
|
||||
outputRound = domain_data[PW_ROUND]
|
||||
|
||||
pw_weather = PirateWeather(
|
||||
name, unique_id, forecast_mode, weather_coordinator, outputRound
|
||||
)
|
||||
|
||||
async_add_entities([pw_weather], False)
|
||||
# _LOGGER.info(pw_weather.__dict__)
|
||||
|
||||
|
||||
class PirateWeather(SingleCoordinatorWeatherEntity[WeatherUpdateCoordinator]):
|
||||
"""Implementation of an Pirate Weather sensor."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_should_poll = False
|
||||
_attr_supported_features = (
|
||||
WeatherEntityFeature.FORECAST_DAILY
|
||||
| WeatherEntityFeature.FORECAST_TWICE_DAILY
|
||||
| WeatherEntityFeature.FORECAST_HOURLY
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
unique_id,
|
||||
forecast_mode: str,
|
||||
weather_coordinator: WeatherUpdateCoordinator,
|
||||
outputRound: str,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(weather_coordinator)
|
||||
self._attr_name = name
|
||||
self._weather_coordinator = weather_coordinator
|
||||
self._name = name
|
||||
self._mode = forecast_mode
|
||||
self._unique_id = unique_id
|
||||
self._ds_data = self._weather_coordinator.data
|
||||
self._ds_currently = self._weather_coordinator.data.currently()
|
||||
self._ds_hourly = self._weather_coordinator.data.hourly()
|
||||
self._ds_daily = self._weather_coordinator.data.daily()
|
||||
|
||||
self.outputRound = outputRound
|
||||
|
||||
units = WEATHER_UNITS.get(
|
||||
self._weather_coordinator.requested_units, WEATHER_UNITS["si"]
|
||||
)
|
||||
self._attr_native_temperature_unit = units["temperature"]
|
||||
self._attr_native_wind_speed_unit = units["wind_speed"]
|
||||
self._attr_native_pressure_unit = units["pressure"]
|
||||
self._attr_native_precipitation_unit = units["precipitation"]
|
||||
self._attr_native_visibility_unit = units["visibility"]
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if weather data is available from Pirate Weather."""
|
||||
return self._weather_coordinator.data is not None
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return ATTRIBUTION
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def native_temperature(self):
|
||||
"""Return the temperature."""
|
||||
temperature = self._weather_coordinator.data.currently().d.get("temperature")
|
||||
|
||||
return round(temperature, 2) if temperature != -999 else None
|
||||
|
||||
@property
|
||||
def native_apparent_temperature(self):
|
||||
"""Return the apparent temperature."""
|
||||
native_apparent_temperature = self._weather_coordinator.data.currently().d.get(
|
||||
"apparentTemperature"
|
||||
)
|
||||
|
||||
return (
|
||||
round(native_apparent_temperature, 2)
|
||||
if native_apparent_temperature != -999
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def cloud_coverage(self):
|
||||
"""Return the cloud coverage."""
|
||||
cloudCover = (
|
||||
self._weather_coordinator.data.currently().d.get("cloudCover") * 100.0
|
||||
)
|
||||
|
||||
return round(cloudCover, 2) if cloudCover != -999 else None
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
"""Return the humidity."""
|
||||
humidity = self._weather_coordinator.data.currently().d.get("humidity") * 100.0
|
||||
|
||||
return round(humidity, 2) if humidity != -999 else None
|
||||
|
||||
@property
|
||||
def native_dew_point(self):
|
||||
"""Return the dew point."""
|
||||
native_dew_point = self._weather_coordinator.data.currently().d.get("dewPoint")
|
||||
|
||||
return round(native_dew_point, 2) if native_dew_point != -999 else None
|
||||
|
||||
@property
|
||||
def native_wind_speed(self):
|
||||
"""Return the wind speed."""
|
||||
windspeed = self._weather_coordinator.data.currently().d.get("windSpeed")
|
||||
|
||||
return round(windspeed, 2) if windspeed != -999 else None
|
||||
|
||||
@property
|
||||
def native_wind_gust_speed(self):
|
||||
"""Return the wind gust speed."""
|
||||
windGust = self._weather_coordinator.data.currently().d.get("windGust")
|
||||
|
||||
return round(windGust, 2) if windGust != -999 else None
|
||||
|
||||
@property
|
||||
def wind_bearing(self):
|
||||
"""Return the wind bearing."""
|
||||
windBearing = self._weather_coordinator.data.currently().d.get("windBearing")
|
||||
|
||||
return windBearing if windBearing != -999 else None
|
||||
|
||||
@property
|
||||
def ozone(self):
|
||||
"""Return the ozone level."""
|
||||
ozone = self._weather_coordinator.data.currently().d.get("ozone")
|
||||
|
||||
return round(ozone, 2) if ozone != -999 else None
|
||||
|
||||
@property
|
||||
def native_pressure(self):
|
||||
"""Return the pressure."""
|
||||
pressure = self._weather_coordinator.data.currently().d.get("pressure")
|
||||
|
||||
return round(pressure, 2) if pressure != -999 else None
|
||||
|
||||
@property
|
||||
def native_visibility(self):
|
||||
"""Return the visibility."""
|
||||
visibility = self._weather_coordinator.data.currently().d.get("visibility")
|
||||
|
||||
return round(visibility, 2) if visibility != -999 else None
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
"""Return the weather condition."""
|
||||
return MAP_CONDITION.get(
|
||||
self._weather_coordinator.data.currently().d.get("icon")
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_forecast_daily(self) -> list[Forecast] | None:
|
||||
"""Return the daily forecast."""
|
||||
daily_forecast = self._weather_coordinator.data.daily().data
|
||||
if not daily_forecast:
|
||||
return None
|
||||
|
||||
return [
|
||||
_map_daily_forecast(f, self._weather_coordinator.requested_units)
|
||||
for f in daily_forecast
|
||||
]
|
||||
|
||||
@callback
|
||||
def _async_forecast_twice_daily(self) -> list[Forecast] | None:
|
||||
"""Return the twice daily forecast."""
|
||||
day_night_forecast = self._weather_coordinator.data.day_night().data
|
||||
if not day_night_forecast:
|
||||
_LOGGER.debug("No twice daily forecast")
|
||||
return None
|
||||
|
||||
# The API returns twice-daily blocks in alternating order: day, night,
|
||||
# day, night, ... so infer daytime by index parity (even index = day).
|
||||
return [
|
||||
_map_day_night_forecast(
|
||||
f, self._weather_coordinator.requested_units, (i % 2) == 0
|
||||
)
|
||||
for i, f in enumerate(day_night_forecast)
|
||||
]
|
||||
|
||||
@callback
|
||||
def _async_forecast_hourly(self) -> list[Forecast] | None:
|
||||
"""Return the hourly forecast."""
|
||||
hourly_forecast = self._weather_coordinator.data.hourly().data
|
||||
|
||||
if not hourly_forecast:
|
||||
return None
|
||||
|
||||
return [
|
||||
_map_hourly_forecast(f, self._weather_coordinator.requested_units)
|
||||
for f in hourly_forecast
|
||||
]
|
||||
@@ -0,0 +1,115 @@
|
||||
"""Weather data coordinator for the Pirate Weather service."""
|
||||
|
||||
import logging
|
||||
|
||||
import async_timeout
|
||||
from aiohttp import ClientError
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
)
|
||||
from .forecast_models import Forecast
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ATTRIBUTION = "Powered by Pirate Weather"
|
||||
|
||||
|
||||
class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Weather data update coordinator."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key,
|
||||
latitude,
|
||||
longitude,
|
||||
scan_interval,
|
||||
language,
|
||||
endpoint,
|
||||
units,
|
||||
hass,
|
||||
config_entry: ConfigEntry,
|
||||
models: str | None,
|
||||
):
|
||||
"""Initialize coordinator."""
|
||||
self._api_key = api_key
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.scan_interval = scan_interval
|
||||
self.language = language
|
||||
self.endpoint = endpoint
|
||||
self.requested_units = units or "si"
|
||||
self.models = models
|
||||
|
||||
self.data = None
|
||||
self.currently = None
|
||||
self.hourly = None
|
||||
self.daily = None
|
||||
self._connect_error = False
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=scan_interval,
|
||||
config_entry=config_entry,
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Update the data."""
|
||||
data = {}
|
||||
async with async_timeout.timeout(60):
|
||||
try:
|
||||
data = await self._get_pw_weather()
|
||||
except ClientError as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||
return data
|
||||
|
||||
async def _get_pw_weather(self):
|
||||
"""Poll weather data from PW."""
|
||||
|
||||
if self.latitude == 0.0:
|
||||
requestLatitude = self.hass.config.latitude
|
||||
else:
|
||||
requestLatitude = self.latitude
|
||||
|
||||
if self.longitude == 0.0:
|
||||
requestLongitude = self.hass.config.longitude
|
||||
else:
|
||||
requestLongitude = self.longitude
|
||||
|
||||
_LOGGER.debug("Request coordinates: %s, %s", requestLatitude, requestLongitude)
|
||||
|
||||
forecastString = (
|
||||
self.endpoint
|
||||
+ "/forecast/"
|
||||
+ self._api_key
|
||||
+ "/"
|
||||
+ str(requestLatitude)
|
||||
+ ","
|
||||
+ str(requestLongitude)
|
||||
+ "?units="
|
||||
+ self.requested_units
|
||||
+ "&extend=hourly"
|
||||
+ "&version=2"
|
||||
+ "&lang="
|
||||
+ self.language
|
||||
+ "&include=day_night_forecast"
|
||||
)
|
||||
if self.models:
|
||||
exclusions = ",".join(
|
||||
m.strip() for m in self.models.split(",") if m.strip()
|
||||
)
|
||||
if exclusions:
|
||||
forecastString += "&exclude=" + exclusions
|
||||
|
||||
session = async_get_clientsession(self.hass)
|
||||
async with session.get(forecastString) as resp:
|
||||
resp.raise_for_status()
|
||||
jsonText = await resp.json()
|
||||
headers = resp.headers
|
||||
_LOGGER.debug("Pirate Weather data update from: %s", self.endpoint)
|
||||
return Forecast(jsonText, resp, headers)
|
||||
Reference in New Issue
Block a user