1471 lines
51 KiB
Python
1471 lines
51 KiB
Python
"""Support for Pirate Weather (Dark Sky Compatable) weather service."""
|
|
|
|
import datetime
|
|
import logging
|
|
from dataclasses import dataclass, field
|
|
from typing import Literal, NamedTuple
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
|
import voluptuous as vol
|
|
from homeassistant.components.sensor import (
|
|
PLATFORM_SCHEMA,
|
|
SensorDeviceClass,
|
|
SensorEntity,
|
|
SensorEntityDescription,
|
|
SensorStateClass,
|
|
)
|
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
|
from homeassistant.const import (
|
|
ATTR_ATTRIBUTION,
|
|
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
CONF_API_KEY,
|
|
CONF_LATITUDE,
|
|
CONF_LONGITUDE,
|
|
CONF_MONITORED_CONDITIONS,
|
|
CONF_NAME,
|
|
CONF_SCAN_INTERVAL,
|
|
DEGREE,
|
|
PERCENTAGE,
|
|
UV_INDEX,
|
|
UnitOfIrradiance,
|
|
UnitOfLength,
|
|
UnitOfPressure,
|
|
UnitOfSpeed,
|
|
UnitOfTemperature,
|
|
UnitOfVolumetricFlux,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.helpers.typing import DiscoveryInfoType, StateType
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
from .const import (
|
|
ALL_CONDITIONS,
|
|
DEFAULT_SCAN_INTERVAL,
|
|
DOMAIN,
|
|
ENTRY_WEATHER_COORDINATOR,
|
|
MANUFACTURER,
|
|
PW_PLATFORM,
|
|
PW_PLATFORMS,
|
|
PW_PREVPLATFORM,
|
|
PW_ROUND,
|
|
)
|
|
from .weather_update_coordinator import WeatherUpdateCoordinator
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
ATTRIBUTION = "Powered by Pirate Weather"
|
|
|
|
CONF_FORECAST = "forecast"
|
|
CONF_HOURLY_FORECAST = "hourly_forecast"
|
|
CONF_LANGUAGE = "language"
|
|
CONF_UNITS = "units"
|
|
|
|
DEFAULT_LANGUAGE = "en"
|
|
DEFAULT_NAME = "Pirate Weather"
|
|
|
|
DEPRECATED_SENSOR_TYPES = {
|
|
"apparent_temperature_max",
|
|
"apparent_temperature_min",
|
|
"temperature_max",
|
|
"temperature_min",
|
|
}
|
|
|
|
MAP_UNIT_SYSTEM: dict[
|
|
Literal["si", "us", "ca", "uk", "uk2"],
|
|
Literal["si_unit", "us_unit", "ca_unit", "uk_unit", "uk2_unit"],
|
|
] = {
|
|
"si": "si_unit",
|
|
"us": "us_unit",
|
|
"ca": "ca_unit",
|
|
"uk": "uk_unit",
|
|
"uk2": "uk2_unit",
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class PirateWeatherSensorEntityDescription(SensorEntityDescription):
|
|
"""Describes Pirate Weather sensor entity."""
|
|
|
|
si_unit: str | None = None
|
|
us_unit: str | None = None
|
|
ca_unit: str | None = None
|
|
uk_unit: str | None = None
|
|
uk2_unit: str | None = None
|
|
forecast_mode: list[str] = field(default_factory=list)
|
|
|
|
|
|
# Sensor Types
|
|
SENSOR_TYPES: dict[str, PirateWeatherSensorEntityDescription] = {
|
|
"summary": PirateWeatherSensorEntityDescription(
|
|
key="summary",
|
|
name="Summary",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"minutely_summary": PirateWeatherSensorEntityDescription(
|
|
key="minutely_summary",
|
|
name="Minutely Summary",
|
|
forecast_mode=[],
|
|
),
|
|
"hourly_summary": PirateWeatherSensorEntityDescription(
|
|
key="hourly_summary",
|
|
name="Hourly Summary",
|
|
forecast_mode=[],
|
|
),
|
|
"daily_summary": PirateWeatherSensorEntityDescription(
|
|
key="daily_summary",
|
|
name="Daily Summary",
|
|
forecast_mode=[],
|
|
),
|
|
"icon": PirateWeatherSensorEntityDescription(
|
|
key="icon",
|
|
name="Icon",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"nearest_storm_distance": PirateWeatherSensorEntityDescription(
|
|
key="nearest_storm_distance",
|
|
name="Nearest Storm Distance",
|
|
si_unit=UnitOfLength.KILOMETERS,
|
|
us_unit=UnitOfLength.MILES,
|
|
ca_unit=UnitOfLength.KILOMETERS,
|
|
uk_unit=UnitOfLength.KILOMETERS,
|
|
uk2_unit=UnitOfLength.MILES,
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-lightning",
|
|
forecast_mode=["currently"],
|
|
),
|
|
"nearest_storm_bearing": PirateWeatherSensorEntityDescription(
|
|
key="nearest_storm_bearing",
|
|
name="Nearest Storm Bearing",
|
|
si_unit=DEGREE,
|
|
us_unit=DEGREE,
|
|
ca_unit=DEGREE,
|
|
uk_unit=DEGREE,
|
|
uk2_unit=DEGREE,
|
|
suggested_display_precision=0,
|
|
icon="mdi:weather-lightning",
|
|
forecast_mode=["currently"],
|
|
),
|
|
"precip_type": PirateWeatherSensorEntityDescription(
|
|
key="precip_type",
|
|
name="Precip",
|
|
icon="mdi:weather-pouring",
|
|
forecast_mode=["currently", "minutely", "hourly", "daily"],
|
|
),
|
|
"precip_intensity": PirateWeatherSensorEntityDescription(
|
|
key="precip_intensity",
|
|
name="Precip Intensity",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-rainy",
|
|
forecast_mode=["currently", "minutely", "hourly", "daily"],
|
|
),
|
|
"precip_probability": PirateWeatherSensorEntityDescription(
|
|
key="precip_probability",
|
|
name="Precip Probability",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=PERCENTAGE,
|
|
us_unit=PERCENTAGE,
|
|
ca_unit=PERCENTAGE,
|
|
uk_unit=PERCENTAGE,
|
|
uk2_unit=PERCENTAGE,
|
|
suggested_display_precision=0,
|
|
icon="mdi:water-percent",
|
|
forecast_mode=["currently", "minutely", "hourly", "daily"],
|
|
),
|
|
"precip_accumulation": PirateWeatherSensorEntityDescription(
|
|
key="precip_accumulation",
|
|
name="Precip Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy",
|
|
forecast_mode=["hourly", "daily"],
|
|
),
|
|
"liquid_accumulation": PirateWeatherSensorEntityDescription(
|
|
key="liquid_accumulation",
|
|
name="Liquid Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-rainy",
|
|
forecast_mode=["hourly", "daily"],
|
|
),
|
|
"snow_accumulation": PirateWeatherSensorEntityDescription(
|
|
key="snow_accumulation",
|
|
name="Snow Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy",
|
|
forecast_mode=["hourly", "daily"],
|
|
),
|
|
"ice_accumulation": PirateWeatherSensorEntityDescription(
|
|
key="ice_accumulation",
|
|
name="Ice Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy-rainy",
|
|
forecast_mode=["hourly", "daily"],
|
|
),
|
|
"current_day_liquid": PirateWeatherSensorEntityDescription(
|
|
key="current_day_liquid",
|
|
name="Current Day Liquid Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-rainy",
|
|
forecast_mode=["currently"],
|
|
),
|
|
"current_day_snow": PirateWeatherSensorEntityDescription(
|
|
key="current_day_snow",
|
|
name="Current Day Snow Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy",
|
|
forecast_mode=["currently"],
|
|
),
|
|
"current_day_ice": PirateWeatherSensorEntityDescription(
|
|
key="current_day_ice",
|
|
name="Current Day Ice Accumulation",
|
|
device_class=SensorDeviceClass.PRECIPITATION,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.CENTIMETERS,
|
|
us_unit=UnitOfLength.INCHES,
|
|
ca_unit=UnitOfLength.CENTIMETERS,
|
|
uk_unit=UnitOfLength.CENTIMETERS,
|
|
uk2_unit=UnitOfLength.CENTIMETERS,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy-rainy",
|
|
forecast_mode=["currently"],
|
|
),
|
|
"temperature": PirateWeatherSensorEntityDescription(
|
|
key="temperature",
|
|
name="Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["currently", "hourly"],
|
|
),
|
|
"apparent_temperature": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature",
|
|
name="Apparent Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["currently", "hourly"],
|
|
),
|
|
"dew_point": PirateWeatherSensorEntityDescription(
|
|
key="dew_point",
|
|
name="Dew Point",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"wind_speed": PirateWeatherSensorEntityDescription(
|
|
key="wind_speed",
|
|
name="Wind Speed",
|
|
device_class=SensorDeviceClass.WIND_SPEED,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfSpeed.METERS_PER_SECOND,
|
|
us_unit=UnitOfSpeed.MILES_PER_HOUR,
|
|
ca_unit=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
|
uk_unit=UnitOfSpeed.MILES_PER_HOUR,
|
|
uk2_unit=UnitOfSpeed.MILES_PER_HOUR,
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-windy",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"wind_bearing": PirateWeatherSensorEntityDescription(
|
|
key="wind_bearing",
|
|
name="Wind Bearing",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=DEGREE,
|
|
us_unit=DEGREE,
|
|
ca_unit=DEGREE,
|
|
uk_unit=DEGREE,
|
|
uk2_unit=DEGREE,
|
|
suggested_display_precision=0,
|
|
icon="mdi:compass",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"wind_gust": PirateWeatherSensorEntityDescription(
|
|
key="wind_gust",
|
|
name="Wind Gust",
|
|
device_class=SensorDeviceClass.WIND_SPEED,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfSpeed.METERS_PER_SECOND,
|
|
us_unit=UnitOfSpeed.MILES_PER_HOUR,
|
|
ca_unit=UnitOfSpeed.KILOMETERS_PER_HOUR,
|
|
uk_unit=UnitOfSpeed.MILES_PER_HOUR,
|
|
uk2_unit=UnitOfSpeed.MILES_PER_HOUR,
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-windy-variant",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"cloud_cover": PirateWeatherSensorEntityDescription(
|
|
key="cloud_cover",
|
|
name="Cloud Coverage",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=PERCENTAGE,
|
|
us_unit=PERCENTAGE,
|
|
ca_unit=PERCENTAGE,
|
|
uk_unit=PERCENTAGE,
|
|
uk2_unit=PERCENTAGE,
|
|
suggested_display_precision=0,
|
|
icon="mdi:weather-partly-cloudy",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"humidity": PirateWeatherSensorEntityDescription(
|
|
key="humidity",
|
|
name="Humidity",
|
|
device_class=SensorDeviceClass.HUMIDITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=PERCENTAGE,
|
|
us_unit=PERCENTAGE,
|
|
ca_unit=PERCENTAGE,
|
|
uk_unit=PERCENTAGE,
|
|
uk2_unit=PERCENTAGE,
|
|
suggested_display_precision=0,
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"pressure": PirateWeatherSensorEntityDescription(
|
|
key="pressure",
|
|
name="Pressure",
|
|
device_class=SensorDeviceClass.PRESSURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfPressure.MBAR,
|
|
us_unit=UnitOfPressure.MBAR,
|
|
ca_unit=UnitOfPressure.MBAR,
|
|
uk_unit=UnitOfPressure.MBAR,
|
|
uk2_unit=UnitOfPressure.MBAR,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"visibility": PirateWeatherSensorEntityDescription(
|
|
key="visibility",
|
|
name="Visibility",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfLength.KILOMETERS,
|
|
us_unit=UnitOfLength.MILES,
|
|
ca_unit=UnitOfLength.KILOMETERS,
|
|
uk_unit=UnitOfLength.KILOMETERS,
|
|
uk2_unit=UnitOfLength.MILES,
|
|
suggested_display_precision=2,
|
|
icon="mdi:eye",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"ozone": PirateWeatherSensorEntityDescription(
|
|
key="ozone",
|
|
name="Ozone",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit="DU",
|
|
us_unit="DU",
|
|
ca_unit="DU",
|
|
uk_unit="DU",
|
|
uk2_unit="DU",
|
|
suggested_display_precision=2,
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"fire_index": PirateWeatherSensorEntityDescription(
|
|
key="fire_index",
|
|
name="Fire Index",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
suggested_display_precision=2,
|
|
icon="mdi:fire",
|
|
forecast_mode=["currently", "hourly"],
|
|
),
|
|
"fire_risk_level": PirateWeatherSensorEntityDescription(
|
|
key="fire_risk_level",
|
|
name="Fire Risk Level",
|
|
device_class=SensorDeviceClass.ENUM,
|
|
icon="mdi:fire",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
options=["Extreme", "Very High", "High", "Moderate", "Low", "N/A"],
|
|
),
|
|
"fire_index_max": PirateWeatherSensorEntityDescription(
|
|
key="fire_index_max",
|
|
name="Fire Index Max",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
suggested_display_precision=2,
|
|
icon="mdi:fire",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"smoke": PirateWeatherSensorEntityDescription(
|
|
key="smoke",
|
|
name="Smoke",
|
|
device_class=SensorDeviceClass.PM25,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
us_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
ca_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
uk_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
uk2_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
suggested_display_precision=2,
|
|
icon="mdi:smoke",
|
|
forecast_mode=["currently", "hourly"],
|
|
),
|
|
"smoke_max": PirateWeatherSensorEntityDescription(
|
|
key="smoke_max",
|
|
name="Smoke Max",
|
|
device_class=SensorDeviceClass.PM25,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
us_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
ca_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
uk_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
uk2_unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
|
suggested_display_precision=2,
|
|
icon="mdi:smoke",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"apparent_temperature_max": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature_max",
|
|
name="Daily High Apparent Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"apparent_temperature_high": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature_high",
|
|
name="Daytime High Apparent Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"apparent_temperature_high_time": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature_high_time",
|
|
name="Daytime High Apparent Temperature Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"apparent_temperature_min": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature_min",
|
|
name="Daily Low Apparent Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"apparent_temperature_low": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature_low",
|
|
name="Overnight Low Apparent Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"apparent_temperature_low_time": PirateWeatherSensorEntityDescription(
|
|
key="apparent_temperature_low_time",
|
|
name="Overnight Low Apparent Temperature Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"temperature_max": PirateWeatherSensorEntityDescription(
|
|
key="temperature_max",
|
|
name="Daily High Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"temperature_high": PirateWeatherSensorEntityDescription(
|
|
key="temperature_high",
|
|
name="Daytime High Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"temperature_high_time": PirateWeatherSensorEntityDescription(
|
|
key="temperature_high_time",
|
|
name="Daytime High Temperature Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"temperature_min": PirateWeatherSensorEntityDescription(
|
|
key="temperature_min",
|
|
name="Daily Low Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"temperature_min_time": PirateWeatherSensorEntityDescription(
|
|
key="temperature_min_time",
|
|
name="Daily Low Temperature Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"temperature_low": PirateWeatherSensorEntityDescription(
|
|
key="temperature_low",
|
|
name="Overnight Low Temperature",
|
|
device_class=SensorDeviceClass.TEMPERATURE,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfTemperature.CELSIUS,
|
|
us_unit=UnitOfTemperature.FAHRENHEIT,
|
|
ca_unit=UnitOfTemperature.CELSIUS,
|
|
uk_unit=UnitOfTemperature.CELSIUS,
|
|
uk2_unit=UnitOfTemperature.CELSIUS,
|
|
suggested_display_precision=2,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"precip_intensity_max": PirateWeatherSensorEntityDescription(
|
|
key="precip_intensity_max",
|
|
name="Daily Max Precip Intensity",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=2,
|
|
icon="mdi:thermometer",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"uv_index": PirateWeatherSensorEntityDescription(
|
|
key="uv_index",
|
|
name="UV Index",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UV_INDEX,
|
|
us_unit=UV_INDEX,
|
|
ca_unit=UV_INDEX,
|
|
uk_unit=UV_INDEX,
|
|
uk2_unit=UV_INDEX,
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-sunny",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"air_quality_index": PirateWeatherSensorEntityDescription(
|
|
key="air_quality_index",
|
|
name="Air Quality Index",
|
|
device_class=SensorDeviceClass.AQI,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
suggested_display_precision=0,
|
|
icon="mdi:air-filter",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"cape": PirateWeatherSensorEntityDescription(
|
|
key="cape",
|
|
name="Convective Available Potential Energy",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
suggested_display_precision=0,
|
|
icon="mdi:weather-lightning",
|
|
forecast_mode=["currently", "hourly"],
|
|
),
|
|
"cape_max": PirateWeatherSensorEntityDescription(
|
|
key="cape_max",
|
|
name="Convective Available Potential Energy Max",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
suggested_display_precision=0,
|
|
icon="mdi:weather-lightning",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"solar": PirateWeatherSensorEntityDescription(
|
|
key="solar",
|
|
name="Downward Short-Wave Radiation Flux",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
us_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
ca_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
uk_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
uk2_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-sunny",
|
|
forecast_mode=["currently", "hourly"],
|
|
),
|
|
"solar_max": PirateWeatherSensorEntityDescription(
|
|
key="solar_max",
|
|
name="Downward Short-Wave Radiation Flux Max",
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
us_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
ca_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
uk_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
uk2_unit=UnitOfIrradiance.WATTS_PER_SQUARE_METER,
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-sunny",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"rain_intensity": PirateWeatherSensorEntityDescription(
|
|
key="rain_intensity",
|
|
name="Rain Intensity",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-rainy",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"rain_intensity_max": PirateWeatherSensorEntityDescription(
|
|
key="rain_intensity_max",
|
|
name="Rain Intensity Max",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-rainy",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"snow_intensity": PirateWeatherSensorEntityDescription(
|
|
key="snow_intensity",
|
|
name="Snow Intensity",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"snow_intensity_max": PirateWeatherSensorEntityDescription(
|
|
key="snow_intensity_max",
|
|
name="Snow Intensity Max",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"ice_intensity": PirateWeatherSensorEntityDescription(
|
|
key="ice_intensity",
|
|
name="Ice Intensity",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy-rainy",
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"ice_intensity_max": PirateWeatherSensorEntityDescription(
|
|
key="ice_intensity_max",
|
|
name="Ice Intensity Max",
|
|
device_class=SensorDeviceClass.PRECIPITATION_INTENSITY,
|
|
state_class=SensorStateClass.MEASUREMENT,
|
|
si_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
us_unit=UnitOfVolumetricFlux.INCHES_PER_HOUR,
|
|
ca_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
uk2_unit=UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
|
|
suggested_display_precision=4,
|
|
icon="mdi:weather-snowy-rainy",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"moon_phase": PirateWeatherSensorEntityDescription(
|
|
key="moon_phase",
|
|
name="Moon Phase",
|
|
suggested_display_precision=2,
|
|
icon="mdi:weather-night",
|
|
forecast_mode=["daily"],
|
|
),
|
|
"sunrise_time": PirateWeatherSensorEntityDescription(
|
|
key="sunrise_time",
|
|
name="Sunrise",
|
|
icon="mdi:white-balance-sunny",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"sunset_time": PirateWeatherSensorEntityDescription(
|
|
key="sunset_time",
|
|
name="Sunset",
|
|
icon="mdi:weather-night",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["daily"],
|
|
),
|
|
"alerts": PirateWeatherSensorEntityDescription(
|
|
key="alerts",
|
|
name="Alerts",
|
|
icon="mdi:alert-circle-outline",
|
|
forecast_mode=[],
|
|
),
|
|
"time": PirateWeatherSensorEntityDescription(
|
|
key="time",
|
|
name="Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=["currently", "hourly", "daily"],
|
|
),
|
|
"hrrr_subh_update_time": PirateWeatherSensorEntityDescription(
|
|
key="hrrr_subh",
|
|
name="HRRR SubHourly Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
"hrrr_0_18_update_time": PirateWeatherSensorEntityDescription(
|
|
key="hrrr_0-18",
|
|
name="HRRR 0-18 Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
"nbm_update_time": PirateWeatherSensorEntityDescription(
|
|
key="nbm",
|
|
name="NBM Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
"nbm_fire_update_time": PirateWeatherSensorEntityDescription(
|
|
key="nbm_fire",
|
|
name="NBM Fire Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
"hrrr_18_48_update_time": PirateWeatherSensorEntityDescription(
|
|
key="hrrr_18-48",
|
|
name="HRRR 18-48 Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
"gfs_update_time": PirateWeatherSensorEntityDescription(
|
|
key="gfs",
|
|
name="GFS Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
"gefs_update_time": PirateWeatherSensorEntityDescription(
|
|
key="gefs",
|
|
name="GEFS Update Time",
|
|
icon="mdi:clock-time-three-outline",
|
|
device_class=SensorDeviceClass.TIMESTAMP,
|
|
forecast_mode=[],
|
|
),
|
|
}
|
|
|
|
|
|
class ConditionPicture(NamedTuple):
|
|
"""Entity picture and icon for condition."""
|
|
|
|
entity_picture: str
|
|
icon: str
|
|
|
|
|
|
CONDITION_PICTURES: dict[str, ConditionPicture] = {
|
|
"clear-day": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-sunny.svg",
|
|
icon="mdi:weather-sunny",
|
|
),
|
|
"clear-night": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-night.svg",
|
|
icon="mdi:weather-night",
|
|
),
|
|
"rain": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-pouring.svg",
|
|
icon="mdi:weather-pouring",
|
|
),
|
|
"snow": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-snowy.svg",
|
|
icon="mdi:weather-snowy",
|
|
),
|
|
"sleet": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-hail.svg",
|
|
icon="mdi:weather-snowy-rainy",
|
|
),
|
|
"wind": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-windy.svg",
|
|
icon="mdi:weather-windy",
|
|
),
|
|
"fog": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-fog.svg",
|
|
icon="mdi:weather-fog",
|
|
),
|
|
"cloudy": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-cloudy.svg",
|
|
icon="mdi:weather-cloudy",
|
|
),
|
|
"partly-cloudy-day": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-partlycloudy.svg",
|
|
icon="mdi:weather-partly-cloudy",
|
|
),
|
|
"partly-cloudy-night": ConditionPicture(
|
|
entity_picture="/static/images/darksky/weather-cloudy.svg",
|
|
icon="mdi:weather-night-partly-cloudy",
|
|
),
|
|
}
|
|
|
|
|
|
# Language Supported Codes
|
|
LANGUAGE_CODES = [
|
|
"ar",
|
|
"az",
|
|
"be",
|
|
"bg",
|
|
"bn",
|
|
"bs",
|
|
"ca",
|
|
"cs",
|
|
"da",
|
|
"de",
|
|
"el",
|
|
"en",
|
|
"ja",
|
|
"ka",
|
|
"kn",
|
|
"ko",
|
|
"eo",
|
|
"es",
|
|
"et",
|
|
"fi",
|
|
"fr",
|
|
"he",
|
|
"hi",
|
|
"hr",
|
|
"hu",
|
|
"id",
|
|
"is",
|
|
"it",
|
|
"kw",
|
|
"lv",
|
|
"ml",
|
|
"mr",
|
|
"nb",
|
|
"nl",
|
|
"pa",
|
|
"pl",
|
|
"pt",
|
|
"ro",
|
|
"ru",
|
|
"sk",
|
|
"sl",
|
|
"sr",
|
|
"sv",
|
|
"ta",
|
|
"te",
|
|
"tet",
|
|
"tr",
|
|
"uk",
|
|
"ur",
|
|
"x-pig-latin",
|
|
"zh",
|
|
"zh-tw",
|
|
]
|
|
|
|
ALLOWED_UNITS = ["si", "us", "ca", "uk", "uk2"]
|
|
|
|
ALERTS_ATTRS = ["time", "description", "expires", "severity", "uri", "regions", "title"]
|
|
|
|
HOURS = list(range(168))
|
|
DAYS = list(range(7))
|
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|
{
|
|
vol.Required(CONF_API_KEY): cv.string,
|
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
|
vol.Optional(CONF_UNITS): vol.In(ALLOWED_UNITS),
|
|
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(LANGUAGE_CODES),
|
|
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): cv.time_period,
|
|
vol.Inclusive(
|
|
CONF_LATITUDE, "coordinates", "Latitude and longitude must exist together"
|
|
): cv.latitude,
|
|
vol.Inclusive(
|
|
CONF_LONGITUDE, "coordinates", "Latitude and longitude must exist together"
|
|
): cv.longitude,
|
|
vol.Optional(PW_PLATFORM): cv.multi_select(PW_PLATFORMS),
|
|
vol.Optional(PW_PREVPLATFORM): cv.string,
|
|
vol.Optional(CONF_FORECAST): cv.multi_select(DAYS),
|
|
vol.Optional(CONF_HOURLY_FORECAST): cv.multi_select(HOURS),
|
|
vol.Optional(CONF_MONITORED_CONDITIONS, default=None): cv.multi_select(
|
|
ALL_CONDITIONS
|
|
),
|
|
}
|
|
)
|
|
|
|
|
|
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 sensor in YAML is deprecated "
|
|
"Your existing configuration has been imported into the UI automatically "
|
|
"and can be safely removed from your configuration.yaml file"
|
|
)
|
|
|
|
# Define as a sensor platform
|
|
config_entry[PW_PLATFORM] = [PW_PLATFORMS[0]]
|
|
|
|
# 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
|
|
)
|
|
)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: ConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up Pirate Weather sensor entities 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]
|
|
conditions = domain_data[CONF_MONITORED_CONDITIONS]
|
|
forecast_days = domain_data[CONF_FORECAST]
|
|
forecast_hours = domain_data[CONF_HOURLY_FORECAST]
|
|
service_id = config_entry.unique_id or config_entry.entry_id
|
|
|
|
# Round Output
|
|
outputRound = domain_data[PW_ROUND]
|
|
|
|
sensors: list[PirateWeatherSensor] = []
|
|
|
|
for condition in conditions:
|
|
# Save units for conversion later
|
|
requestUnits = domain_data[CONF_UNITS]
|
|
|
|
sensorDescription = SENSOR_TYPES[condition]
|
|
|
|
if condition in DEPRECATED_SENSOR_TYPES:
|
|
_LOGGER.warning("Monitored condition %s is deprecated", condition)
|
|
|
|
if (
|
|
not sensorDescription.forecast_mode
|
|
or "currently" in sensorDescription.forecast_mode
|
|
):
|
|
unique_id = f"{config_entry.unique_id}-sensor-{condition}"
|
|
sensors.append(
|
|
PirateWeatherSensor(
|
|
weather_coordinator,
|
|
condition,
|
|
name,
|
|
unique_id,
|
|
forecast_day=None,
|
|
forecast_hour=None,
|
|
description=sensorDescription,
|
|
requestUnits=requestUnits,
|
|
outputRound=outputRound,
|
|
service_id=service_id,
|
|
)
|
|
)
|
|
|
|
if forecast_days is not None and "daily" in sensorDescription.forecast_mode:
|
|
for forecast_day in forecast_days:
|
|
unique_id = (
|
|
f"{config_entry.unique_id}-sensor-{condition}-daily-{forecast_day}"
|
|
)
|
|
sensors.append(
|
|
PirateWeatherSensor(
|
|
weather_coordinator,
|
|
condition,
|
|
name,
|
|
unique_id,
|
|
forecast_day=int(forecast_day),
|
|
forecast_hour=None,
|
|
description=sensorDescription,
|
|
requestUnits=requestUnits,
|
|
outputRound=outputRound,
|
|
service_id=service_id,
|
|
)
|
|
)
|
|
|
|
if forecast_hours is not None and "hourly" in sensorDescription.forecast_mode:
|
|
for forecast_h in forecast_hours:
|
|
unique_id = (
|
|
f"{config_entry.unique_id}-sensor-{condition}-hourly-{forecast_h}"
|
|
)
|
|
sensors.append(
|
|
PirateWeatherSensor(
|
|
weather_coordinator,
|
|
condition,
|
|
name,
|
|
unique_id,
|
|
forecast_day=None,
|
|
forecast_hour=int(forecast_h),
|
|
description=sensorDescription,
|
|
requestUnits=requestUnits,
|
|
outputRound=outputRound,
|
|
service_id=service_id,
|
|
)
|
|
)
|
|
|
|
async_add_entities(sensors)
|
|
|
|
|
|
class PirateWeatherSensor(SensorEntity):
|
|
"""Class for an Pirate Weather sensor."""
|
|
|
|
# _attr_should_poll = False
|
|
_attr_attribution = ATTRIBUTION
|
|
entity_description: PirateWeatherSensorEntityDescription
|
|
|
|
def __init__(
|
|
self,
|
|
weather_coordinator: WeatherUpdateCoordinator,
|
|
condition: str,
|
|
name: str,
|
|
unique_id,
|
|
forecast_day: int,
|
|
forecast_hour: int,
|
|
description: PirateWeatherSensorEntityDescription,
|
|
requestUnits: str,
|
|
outputRound: str,
|
|
service_id: str,
|
|
) -> None:
|
|
"""Initialize the sensor."""
|
|
self.client_name = name
|
|
|
|
self.entity_description = description
|
|
self.description = description
|
|
|
|
self._weather_coordinator = weather_coordinator
|
|
|
|
self._attr_unique_id = unique_id
|
|
self._attr_name = name
|
|
self._attr_device_info = DeviceInfo(
|
|
entry_type=DeviceEntryType.SERVICE,
|
|
identifiers={(DOMAIN, service_id)},
|
|
manufacturer=MANUFACTURER,
|
|
name=name,
|
|
)
|
|
|
|
self.forecast_day = forecast_day
|
|
self.forecast_hour = forecast_hour
|
|
self.requestUnits = requestUnits
|
|
self.outputRound = outputRound
|
|
self.type = condition
|
|
self._icon = None
|
|
self._alerts = None
|
|
|
|
self._name = description.name
|
|
|
|
@property
|
|
def name(self):
|
|
"""Return the name of the sensor."""
|
|
if self.forecast_day is not None:
|
|
return f"{self.client_name} {self._name} {self.forecast_day}d"
|
|
if self.forecast_hour is not None:
|
|
return f"{self.client_name} {self._name} {self.forecast_hour}h"
|
|
return f"{self.client_name} {self._name}"
|
|
|
|
@property
|
|
def available(self) -> bool:
|
|
"""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 native_unit_of_measurement(self):
|
|
"""Return the unit of measurement of this entity, if any."""
|
|
unit_key = MAP_UNIT_SYSTEM.get(self.unit_system, "si_unit")
|
|
self._attr_native_unit_of_measurement = getattr(
|
|
self.entity_description, unit_key
|
|
)
|
|
return self._attr_native_unit_of_measurement
|
|
|
|
@property
|
|
def unit_system(self):
|
|
"""Return the unit system of this entity."""
|
|
return self.requestUnits
|
|
|
|
@property
|
|
def entity_picture(self) -> str | None:
|
|
"""Return the entity picture to use in the frontend, if any."""
|
|
if self._icon is None or "summary" not in self.entity_description.key:
|
|
return None
|
|
|
|
if self._icon in CONDITION_PICTURES:
|
|
return CONDITION_PICTURES[self._icon].entity_picture
|
|
|
|
return None
|
|
|
|
def update_unit_of_measurement(self) -> None:
|
|
"""Update units based on unit system."""
|
|
unit_key = MAP_UNIT_SYSTEM.get(self.unit_system, "si_unit")
|
|
self._attr_native_unit_of_measurement = getattr(
|
|
self.entity_description, unit_key
|
|
)
|
|
|
|
@property
|
|
def icon(self) -> str | None:
|
|
"""Icon to use in the frontend, if any."""
|
|
if (
|
|
"summary" in self.entity_description.key
|
|
and self._icon in CONDITION_PICTURES
|
|
):
|
|
return CONDITION_PICTURES[self._icon].icon
|
|
|
|
return self.entity_description.icon
|
|
|
|
@property
|
|
def extra_state_attributes(self):
|
|
"""Return the state attributes."""
|
|
if self.type == "alerts":
|
|
extraATTR = self._alerts
|
|
extraATTR[ATTR_ATTRIBUTION] = ATTRIBUTION
|
|
else:
|
|
extraATTR = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
|
return extraATTR
|
|
|
|
@property
|
|
def native_value(self) -> StateType:
|
|
"""Return the state of the device."""
|
|
self.update_unit_of_measurement()
|
|
|
|
if self.type == "alerts":
|
|
data = self._weather_coordinator.data.alerts()
|
|
|
|
alerts = {}
|
|
if data is None:
|
|
self._alerts = alerts
|
|
return data
|
|
|
|
multiple_alerts = len(data) > 1
|
|
for i, alert in enumerate(data):
|
|
for attr in ALERTS_ATTRS:
|
|
if multiple_alerts:
|
|
dkey = f"{attr}_{i!s}"
|
|
else:
|
|
dkey = attr
|
|
alertsAttr = getattr(alert, attr)
|
|
|
|
# Convert time to string using dt_util
|
|
if isinstance(alertsAttr, int):
|
|
alertsAttr = dt_util.as_local(
|
|
dt_util.utc_from_timestamp(alertsAttr)
|
|
).isoformat()
|
|
|
|
alerts[dkey] = alertsAttr
|
|
|
|
self._alerts = alerts
|
|
native_val = len(data)
|
|
|
|
elif self.type in [
|
|
"hrrr_subh_update_time",
|
|
"hrrr_0_18_update_time",
|
|
"nbm_update_time",
|
|
"nbm_fire_update_time",
|
|
"hrrr_18_48_update_time",
|
|
"gfs_update_time",
|
|
"gefs_update_time",
|
|
]:
|
|
try:
|
|
flags = self._weather_coordinator.data.flags()
|
|
model_time_string = flags.sourceTimes[self.entity_description.key]
|
|
native_val = datetime.datetime.strptime(
|
|
model_time_string[0:-1], "%Y-%m-%d %H"
|
|
).replace(tzinfo=datetime.UTC)
|
|
except KeyError:
|
|
native_val = None
|
|
|
|
elif self.type == "minutely_summary":
|
|
native_val = getattr(
|
|
self._weather_coordinator.data.minutely(), "summary", ""
|
|
)
|
|
self._icon = getattr(self._weather_coordinator.data.minutely(), "icon", "")
|
|
elif self.type == "hourly_summary":
|
|
native_val = getattr(self._weather_coordinator.data.hourly(), "summary", "")
|
|
self._icon = getattr(self._weather_coordinator.data.hourly(), "icon", "")
|
|
|
|
elif self.forecast_hour is not None:
|
|
hourly = self._weather_coordinator.data.hourly()
|
|
if hasattr(hourly, "data"):
|
|
native_val = self.get_state(hourly.data[self.forecast_hour].d)
|
|
else:
|
|
native_val = 0
|
|
|
|
elif self.type == "daily_summary":
|
|
native_val = getattr(self._weather_coordinator.data.daily(), "summary", "")
|
|
self._icon = getattr(self._weather_coordinator.data.daily(), "icon", "")
|
|
|
|
elif self.forecast_day is not None:
|
|
daily = self._weather_coordinator.data.daily()
|
|
if hasattr(daily, "data"):
|
|
native_val = self.get_state(daily.data[self.forecast_day].d)
|
|
else:
|
|
native_val = 0
|
|
else:
|
|
currently = self._weather_coordinator.data.currently()
|
|
native_val = self.get_state(currently.d)
|
|
|
|
# self._state = native_val
|
|
|
|
return native_val
|
|
|
|
def get_state(self, data):
|
|
"""Return a new state based on the type.
|
|
|
|
If the sensor type is unknown, the current state is returned.
|
|
"""
|
|
|
|
if self.type == "fire_risk_level":
|
|
if self.forecast_hour is not None:
|
|
state = data.get("fireIndex")
|
|
elif self.forecast_day is not None:
|
|
state = data.get("fireIndexMax")
|
|
else:
|
|
state = data.get("fireIndex")
|
|
else:
|
|
lookup_type = convert_to_camel(self.type)
|
|
state = data.get(lookup_type)
|
|
|
|
# If the sensor is numeric and the data is -999 set return None instead of -999
|
|
if isinstance(state, (int, float)) and state == -999:
|
|
return None
|
|
|
|
if state is None:
|
|
return state
|
|
|
|
if "summary" in self.type:
|
|
self._icon = getattr(data, "icon", "")
|
|
|
|
# If output rounding is requested, round to nearest integer
|
|
if self.outputRound == "Yes":
|
|
roundingVal = 0
|
|
roundingPrecip = 2
|
|
else:
|
|
roundingVal = 2
|
|
roundingPrecip = 4
|
|
|
|
# Some state data needs to be rounded to whole values or converted to
|
|
# percentages
|
|
if self.type in ["precip_probability", "cloud_cover", "humidity"]:
|
|
state = int(state * 100)
|
|
|
|
# Convert unix times to datetimes times
|
|
if self.type in [
|
|
"temperature_high_time",
|
|
"temperature_min_time",
|
|
"apparent_temperature_high_time",
|
|
"apparent_temperature_low_time",
|
|
"sunrise_time",
|
|
"sunset_time",
|
|
"time",
|
|
]:
|
|
outState = datetime.datetime.fromtimestamp(state, datetime.UTC)
|
|
|
|
elif self.type == "fire_risk_level":
|
|
outState = fire_index(state)
|
|
elif self.type in [
|
|
"dew_point",
|
|
"temperature",
|
|
"apparent_temperature",
|
|
"temperature_low",
|
|
"apparent_temperature_low",
|
|
"temperature_min",
|
|
"apparent_temperature_min",
|
|
"temperature_high",
|
|
"apparent_temperature_high",
|
|
"temperature_max",
|
|
"apparent_temperature_max",
|
|
"pressure",
|
|
"ozone",
|
|
"fire_index",
|
|
"fire_index_max",
|
|
"uv_index",
|
|
"air_quality_index",
|
|
"wind_speed",
|
|
"wind_gust",
|
|
"visibility",
|
|
"nearest_storm_distance",
|
|
"smoke",
|
|
"smoke_max",
|
|
"solar",
|
|
"solar_max",
|
|
]:
|
|
if roundingVal == 0:
|
|
outState = int(round(state, roundingVal))
|
|
else:
|
|
outState = round(state, roundingVal)
|
|
|
|
elif self.type in [
|
|
"precip_accumulation",
|
|
"liquid_accumulation",
|
|
"snow_accumulation",
|
|
"ice_accumulation",
|
|
"precip_intensity",
|
|
"precip_intensity_max",
|
|
"current_day_liquid",
|
|
"current_day_snow",
|
|
"current_day_ice",
|
|
"rain_intensity",
|
|
"rain_intensity_max",
|
|
"snow_intensity",
|
|
"snow_intensity_max",
|
|
"ice_intensity",
|
|
"ice_intensity_max",
|
|
]:
|
|
# Convert snow intensity from cm/h to mm/h for non-US units
|
|
if (
|
|
self.type in ["snow_intensity", "snow_intensity_max"]
|
|
and self.unit_system != "us"
|
|
):
|
|
state = state * 10
|
|
outState = round(state, roundingPrecip)
|
|
|
|
else:
|
|
outState = state
|
|
|
|
return outState
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""Connect to dispatcher listening for entity data notifications."""
|
|
self.async_on_remove(
|
|
self._weather_coordinator.async_add_listener(self.async_write_ha_state)
|
|
)
|
|
|
|
# async def async_update(self) -> None:
|
|
# """Get the latest data from PW and updates the states."""
|
|
# await self._weather_coordinator.async_request_refresh()
|
|
|
|
|
|
def convert_to_camel(data):
|
|
"""Convert snake case (foo_bar_bat) to camel case (fooBarBat).
|
|
|
|
This is not pythonic, but needed for certain situations.
|
|
"""
|
|
components = data.split("_")
|
|
capital_components = "".join(x.title() for x in components[1:])
|
|
return f"{components[0]}{capital_components}"
|
|
|
|
|
|
def fire_index(fire_index):
|
|
"""Convert numeric fire index to a textual value."""
|
|
|
|
if fire_index == -999:
|
|
outState = "N/A"
|
|
elif fire_index >= 30:
|
|
outState = "Extreme"
|
|
elif fire_index >= 20:
|
|
outState = "Very High"
|
|
elif fire_index >= 10:
|
|
outState = "High"
|
|
elif fire_index >= 5:
|
|
outState = "Moderate"
|
|
else:
|
|
outState = "Low"
|
|
|
|
return outState
|