"""General constants.""" from __future__ import annotations import base64 import logging import pickle import zlib from collections.abc import Callable from datetime import datetime from homeassistant.components.weather import ( ATTR_WEATHER_TEMPERATURE_UNIT, ATTR_WEATHER_WIND_SPEED_UNIT, UNIT_CONVERSIONS, ) from homeassistant.const import Platform from homeassistant.helpers import selector _LOGGER = logging.getLogger(__name__) DOMAIN = "yandex_pogoda" DEFAULT_NAME = "Yandex Pogoda" ATTRIBUTION = "Data provided by Yandex Pogoda" MANUFACTURER = "Yandex" ENTRY_NAME = "name" UPDATER = "updater" ATTR_API_TEMPERATURE = "temperature" ATTR_API_TEMPERATURE_MAX = "maxTemperature" ATTR_API_TEMPERATURE_MIN = "minTemperature" ATTR_API_FEELS_LIKE_TEMPERATURE = "feelsLike" ATTR_API_WIND_SPEED = "windSpeed" ATTR_API_WIND_BEARING = "windAngle" ATTR_API_DAYTIME = "daytime" ATTR_API_CONDITION = "condition" ATTR_API_IMAGE = "icon" ATTR_API_POLAR = "polar" ATTR_API_SERVER_TIME = "serverTime" ATTR_API_TIME = "time" ATTR_API_SUNRISE_BEGIN_TIME = "sunriseTime" ATTR_API_SUNRISE_END_TIME = "sunsetTime" ATTR_API_YA_CONDITION = "yandex_condition" ATTR_API_WIND_GUST = "windGust" ATTR_FORECAST_DATA = "forecast" ATTR_FORECAST_DATA_COMPRESSED = "forecast_compressed" ATTR_FORECAST_HOURLY = "hourly" ATTR_FORECAST_HOURLY_ICONS = "forecast_hourly_icons" ATTR_FORECAST_TWICE_DAILY = "twice_daily" ATTR_FORECAST_TWICE_DAILY_ICONS = "forecast_twice_daily_icons" ATTR_MIN_FORECAST_TEMPERATURE = "min_forecast_temperature" ATTR_WIND_INTERCARDINAL_DIRECTION = "wind_intercardinal" UPDATE_LISTENER = "update_listener" PLATFORMS = [Platform.SENSOR, Platform.WEATHER] YA_CONDITIONS = [ "CLEAR", "PARTLY_CLOUDY", "CLOUDY", "OVERCAST", "LIGHT_RAIN", "RAIN", "HEAVY_RAIN", "SHOWERS", "SLEET", "LIGHT_SNOW", "SNOW", "SNOWFALL", "HAIL", "THUNDERSTORM", "THUNDERSTORM_WITH_RAIN", "THUNDERSTORM_WITH_HAIL", ] YA_CONDITION_STATE_MAP = {i: i.lower() for i in YA_CONDITIONS} """Mapping for yandex states""" YA_CONDITION_STATES = list(YA_CONDITION_STATE_MAP.values()) WEATHER_STATES_CONVERSION = { "CLEAR": { "day": "sunny", "night": "clear-night", }, "PARTLY_CLOUDY": "partlycloudy", "CLOUDY": "cloudy", "OVERCAST": "cloudy", "LIGHT_RAIN": "rainy", "RAIN": "rainy", "HEAVY_RAIN": "pouring", "SHOWERS": "pouring", "SLEET": "snowy-rainy", "LIGHT_SNOW": "snowy", "SNOW": "snowy", "SNOWFALL": "snowy", "HAIL": "hail", "THUNDERSTORM": "lightning", "THUNDERSTORM_WITH_RAIN": "lightning-rainy", "THUNDERSTORM_WITH_HAIL": "lightning-rainy", } """Map rich Yandex weather condition to ordinary HA""" HA_WEATHER_STATES = [ i for i in WEATHER_STATES_CONVERSION.values() if isinstance(i, str) ] + list(WEATHER_STATES_CONVERSION["CLEAR"].values()) CONDITION_ICONS = { "CLEAR": { "day": "mdi:weather-sunny", "night": "mdi:weather-night", }, "PARTLY_CLOUDY": { "day": "mdi:weather-partly-cloudy", "night": "mdi:weather-night-partly-cloudy", }, "CLOUDY": "mdi:weather-cloudy", "OVERCAST": "mdi:weather-cloudy", "LIGHT_RAIN": "mdi:weather-rainy", "RAIN": "mdi:weather-rainy", "HEAVY_RAIN": "mdi:weather-pouring", "SHOWERS": "mdi:weather-pouring", "SLEET": "mdi:weather-snowy-rainy", "LIGHT_SNOW": "mdi:weather-snowy", "SNOW": "mdi:weather-snowy", "SNOWFALL": "mdi:weather-snowy-heavy", "HAIL": "mdi:weather-hail", "THUNDERSTORM": "mdi:weather-lightning", "THUNDERSTORM_WITH_RAIN": "mdi:weather-lightning-rainy", "THUNDERSTORM_WITH_HAIL": "mdi:weather-lightning-rainy", } """Mapping for state icon""" TEMPERATURE_CONVERTER = UNIT_CONVERSIONS[ATTR_WEATHER_TEMPERATURE_UNIT] WIND_SPEED_CONVERTER = UNIT_CONVERSIONS[ATTR_WEATHER_WIND_SPEED_UNIT] DEFAULT_UPDATES_PER_DAY = "standard" UPDATES_PER_DAY_MAP = { "rare": 6, "standard": 12, "often": 24, } UPDATES_PER_DAY = "updates_per_day" UPDATES_PER_DAY_SELECTOR = selector.SelectSelector( selector.SelectSelectorConfig( options=["rare", "standard", "often"], mode=selector.SelectSelectorMode.DROPDOWN, translation_key=UPDATES_PER_DAY, ) ) def convert_unit_value( converter: Callable[[float, str, str], float], val: float | None, unit_from: str | None, unit_to: str | None, ) -> float | None: """Weather factor unit converter.""" if val is not None and unit_from and unit_to: return converter(val, unit_from, unit_to) return None def map_state(src: str, is_day: bool, mapping: dict | None = None) -> str: """Map weather condition based on WEATHER_STATES_CONVERSION. :param src: str: Yandex weather state :param is_day: bool: Is it day? Used for icons :param mapping: use this dict for mapping :return: str: Home Assistant weather state """ if mapping is None: mapping = {} try: result: str | dict[str, str] = mapping[src] except KeyError: result = src if type(result) is dict: return result["day" if is_day else "night"] return result def get_wind_intercardinal_direction(wind_direction_degree: int) -> str: """Convert wind direction degree to named direction.""" if wind_direction_degree < 0 or wind_direction_degree > 360: _LOGGER.error("Angle should be in [0, 360] range") if wind_direction_degree <= 22.5: return "n" if wind_direction_degree <= 67.5: return "ne" if wind_direction_degree <= 112.5: return "e" if wind_direction_degree <= 157.5: return "se" if wind_direction_degree <= 202.5: return "s" if wind_direction_degree <= 247.5: return "sw" if wind_direction_degree <= 292.5: return "w" if wind_direction_degree <= 337.5: return "nw" return "n" def compress_data(data: dict) -> str: """Compress dict to string.""" return base64.b64encode(zlib.compress(pickle.dumps(data))).decode("utf-8") def decompress_data(compressed: str | None) -> dict: """Compress dict to string.""" if not compressed: return {} try: return pickle.loads(zlib.decompress(base64.b64decode(compressed))) except TypeError: # for backward compatibility return {} def is_daytime( hour: datetime, day: datetime, sunrise_begin: datetime, sunset_end: datetime, polar: str, ) -> bool: """returns whether it is daytime for a given hour of the day""" if polar: if day == sunrise_begin and day == sunset_end: return polar == "DAY" if day == sunrise_begin: return hour < sunset_end if day == sunset_end: return hour >= sunrise_begin if sunrise_begin <= sunset_end: return hour >= sunrise_begin and hour < sunset_end else: return hour < sunset_end or hour >= sunrise_begin