diff --git a/CODEOWNERS b/CODEOWNERS index 88df4edd6fc..e97b10783d6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -228,8 +228,6 @@ build.json @home-assistant/supervisor /homeassistant/components/cups/ @fabaff /homeassistant/components/daikin/ @fredrike /tests/components/daikin/ @fredrike -/homeassistant/components/darksky/ @fabaff -/tests/components/darksky/ @fabaff /homeassistant/components/debugpy/ @frenck /tests/components/debugpy/ @frenck /homeassistant/components/deconz/ @Kane610 diff --git a/homeassistant/components/darksky/__init__.py b/homeassistant/components/darksky/__init__.py deleted file mode 100644 index 90a5d06dc0e..00000000000 --- a/homeassistant/components/darksky/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The darksky component.""" diff --git a/homeassistant/components/darksky/manifest.json b/homeassistant/components/darksky/manifest.json deleted file mode 100644 index 6ff20b11f2a..00000000000 --- a/homeassistant/components/darksky/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "darksky", - "name": "Dark Sky", - "codeowners": ["@fabaff"], - "documentation": "https://www.home-assistant.io/integrations/darksky", - "iot_class": "cloud_polling", - "loggers": ["forecastio"], - "requirements": ["python-forecastio==1.4.0"] -} diff --git a/homeassistant/components/darksky/sensor.py b/homeassistant/components/darksky/sensor.py deleted file mode 100644 index ef1ef0466d4..00000000000 --- a/homeassistant/components/darksky/sensor.py +++ /dev/null @@ -1,927 +0,0 @@ -"""Support for Dark Sky weather service.""" -from __future__ import annotations - -from dataclasses import dataclass, field -from datetime import timedelta -import logging -from typing import Literal, NamedTuple - -import forecastio -from requests.exceptions import ConnectionError as ConnectError, HTTPError, Timeout -import voluptuous as vol - -from homeassistant.components.sensor import ( - PLATFORM_SCHEMA, - SensorDeviceClass, - SensorEntity, - SensorEntityDescription, - SensorStateClass, -) -from homeassistant.const import ( - CONF_API_KEY, - CONF_LATITUDE, - CONF_LONGITUDE, - CONF_MONITORED_CONDITIONS, - CONF_NAME, - CONF_SCAN_INTERVAL, - DEGREE, - PERCENTAGE, - UV_INDEX, - UnitOfLength, - UnitOfPrecipitationDepth, - UnitOfPressure, - UnitOfSpeed, - UnitOfTemperature, - UnitOfVolumetricFlux, -) -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from homeassistant.util import Throttle -from homeassistant.util.unit_system import METRIC_SYSTEM - -_LOGGER = logging.getLogger(__name__) - -CONF_FORECAST = "forecast" -CONF_HOURLY_FORECAST = "hourly_forecast" -CONF_LANGUAGE = "language" -CONF_UNITS = "units" - -DEFAULT_LANGUAGE = "en" -DEFAULT_NAME = "Dark Sky" -SCAN_INTERVAL = timedelta(seconds=300) - -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 DarkskySensorEntityDescription(SensorEntityDescription): - """Describes Darksky 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: dict[str, DarkskySensorEntityDescription] = { - "summary": DarkskySensorEntityDescription( - key="summary", - name="Summary", - forecast_mode=["currently", "hourly", "daily"], - ), - "minutely_summary": DarkskySensorEntityDescription( - key="minutely_summary", - name="Minutely Summary", - forecast_mode=[], - ), - "hourly_summary": DarkskySensorEntityDescription( - key="hourly_summary", - name="Hourly Summary", - forecast_mode=[], - ), - "daily_summary": DarkskySensorEntityDescription( - key="daily_summary", - name="Daily Summary", - forecast_mode=[], - ), - "icon": DarkskySensorEntityDescription( - key="icon", - name="Icon", - forecast_mode=["currently", "hourly", "daily"], - ), - "nearest_storm_distance": DarkskySensorEntityDescription( - 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, - icon="mdi:weather-lightning", - forecast_mode=["currently"], - ), - "nearest_storm_bearing": DarkskySensorEntityDescription( - key="nearest_storm_bearing", - name="Nearest Storm Bearing", - si_unit=DEGREE, - us_unit=DEGREE, - ca_unit=DEGREE, - uk_unit=DEGREE, - uk2_unit=DEGREE, - icon="mdi:weather-lightning", - forecast_mode=["currently"], - ), - "precip_type": DarkskySensorEntityDescription( - key="precip_type", - name="Precip", - icon="mdi:weather-pouring", - forecast_mode=["currently", "minutely", "hourly", "daily"], - ), - "precip_intensity": DarkskySensorEntityDescription( - key="precip_intensity", - name="Precip Intensity", - 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, - icon="mdi:weather-rainy", - forecast_mode=["currently", "minutely", "hourly", "daily"], - ), - "precip_probability": DarkskySensorEntityDescription( - key="precip_probability", - name="Precip Probability", - si_unit=PERCENTAGE, - us_unit=PERCENTAGE, - ca_unit=PERCENTAGE, - uk_unit=PERCENTAGE, - uk2_unit=PERCENTAGE, - icon="mdi:water-percent", - forecast_mode=["currently", "minutely", "hourly", "daily"], - ), - "precip_accumulation": DarkskySensorEntityDescription( - key="precip_accumulation", - name="Precip Accumulation", - device_class=SensorDeviceClass.PRECIPITATION, - si_unit=UnitOfPrecipitationDepth.CENTIMETERS, - us_unit=UnitOfPrecipitationDepth.INCHES, - ca_unit=UnitOfPrecipitationDepth.CENTIMETERS, - uk_unit=UnitOfPrecipitationDepth.CENTIMETERS, - uk2_unit=UnitOfPrecipitationDepth.CENTIMETERS, - icon="mdi:weather-snowy", - forecast_mode=["hourly", "daily"], - ), - "temperature": DarkskySensorEntityDescription( - 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, - forecast_mode=["currently", "hourly"], - ), - "apparent_temperature": DarkskySensorEntityDescription( - 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, - forecast_mode=["currently", "hourly"], - ), - "dew_point": DarkskySensorEntityDescription( - 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, - forecast_mode=["currently", "hourly", "daily"], - ), - "wind_speed": DarkskySensorEntityDescription( - key="wind_speed", - name="Wind Speed", - device_class=SensorDeviceClass.WIND_SPEED, - 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, - forecast_mode=["currently", "hourly", "daily"], - ), - "wind_bearing": DarkskySensorEntityDescription( - key="wind_bearing", - name="Wind Bearing", - si_unit=DEGREE, - us_unit=DEGREE, - ca_unit=DEGREE, - uk_unit=DEGREE, - uk2_unit=DEGREE, - icon="mdi:compass", - forecast_mode=["currently", "hourly", "daily"], - ), - "wind_gust": DarkskySensorEntityDescription( - key="wind_gust", - name="Wind Gust", - device_class=SensorDeviceClass.WIND_SPEED, - 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, - icon="mdi:weather-windy-variant", - forecast_mode=["currently", "hourly", "daily"], - ), - "cloud_cover": DarkskySensorEntityDescription( - key="cloud_cover", - name="Cloud Coverage", - si_unit=PERCENTAGE, - us_unit=PERCENTAGE, - ca_unit=PERCENTAGE, - uk_unit=PERCENTAGE, - uk2_unit=PERCENTAGE, - icon="mdi:weather-partly-cloudy", - forecast_mode=["currently", "hourly", "daily"], - ), - "humidity": DarkskySensorEntityDescription( - 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, - forecast_mode=["currently", "hourly", "daily"], - ), - "pressure": DarkskySensorEntityDescription( - key="pressure", - name="Pressure", - device_class=SensorDeviceClass.PRESSURE, - si_unit=UnitOfPressure.MBAR, - us_unit=UnitOfPressure.MBAR, - ca_unit=UnitOfPressure.MBAR, - uk_unit=UnitOfPressure.MBAR, - uk2_unit=UnitOfPressure.MBAR, - forecast_mode=["currently", "hourly", "daily"], - ), - "visibility": DarkskySensorEntityDescription( - key="visibility", - name="Visibility", - si_unit=UnitOfLength.KILOMETERS, - us_unit=UnitOfLength.MILES, - ca_unit=UnitOfLength.KILOMETERS, - uk_unit=UnitOfLength.KILOMETERS, - uk2_unit=UnitOfLength.MILES, - icon="mdi:eye", - forecast_mode=["currently", "hourly", "daily"], - ), - "ozone": DarkskySensorEntityDescription( - key="ozone", - name="Ozone", - device_class=SensorDeviceClass.OZONE, - si_unit="DU", - us_unit="DU", - ca_unit="DU", - uk_unit="DU", - uk2_unit="DU", - forecast_mode=["currently", "hourly", "daily"], - ), - "apparent_temperature_max": DarkskySensorEntityDescription( - key="apparent_temperature_max", - name="Daily High Apparent Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "apparent_temperature_high": DarkskySensorEntityDescription( - key="apparent_temperature_high", - name="Daytime High Apparent Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "apparent_temperature_min": DarkskySensorEntityDescription( - key="apparent_temperature_min", - name="Daily Low Apparent Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "apparent_temperature_low": DarkskySensorEntityDescription( - key="apparent_temperature_low", - name="Overnight Low Apparent Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "temperature_max": DarkskySensorEntityDescription( - key="temperature_max", - name="Daily High Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "temperature_high": DarkskySensorEntityDescription( - key="temperature_high", - name="Daytime High Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "temperature_min": DarkskySensorEntityDescription( - key="temperature_min", - name="Daily Low Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "temperature_low": DarkskySensorEntityDescription( - key="temperature_low", - name="Overnight Low Temperature", - device_class=SensorDeviceClass.TEMPERATURE, - si_unit=UnitOfTemperature.CELSIUS, - us_unit=UnitOfTemperature.FAHRENHEIT, - ca_unit=UnitOfTemperature.CELSIUS, - uk_unit=UnitOfTemperature.CELSIUS, - uk2_unit=UnitOfTemperature.CELSIUS, - forecast_mode=["daily"], - ), - "precip_intensity_max": DarkskySensorEntityDescription( - key="precip_intensity_max", - name="Daily Max Precip Intensity", - 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, - icon="mdi:thermometer", - forecast_mode=["daily"], - ), - "uv_index": DarkskySensorEntityDescription( - key="uv_index", - name="UV Index", - si_unit=UV_INDEX, - us_unit=UV_INDEX, - ca_unit=UV_INDEX, - uk_unit=UV_INDEX, - uk2_unit=UV_INDEX, - icon="mdi:weather-sunny", - forecast_mode=["currently", "hourly", "daily"], - ), - "moon_phase": DarkskySensorEntityDescription( - key="moon_phase", - name="Moon Phase", - icon="mdi:weather-night", - forecast_mode=["daily"], - ), - "sunrise_time": DarkskySensorEntityDescription( - key="sunrise_time", - name="Sunrise", - icon="mdi:white-balance-sunny", - forecast_mode=["daily"], - ), - "sunset_time": DarkskySensorEntityDescription( - key="sunset_time", - name="Sunset", - icon="mdi:weather-night", - forecast_mode=["daily"], - ), - "alerts": DarkskySensorEntityDescription( - key="alerts", - name="Alerts", - icon="mdi:alert-circle-outline", - 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 = ["auto", "si", "us", "ca", "uk", "uk2"] - -ALERTS_ATTRS = ["time", "description", "expires", "severity", "uri", "regions", "title"] - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_MONITORED_CONDITIONS): vol.All( - cv.ensure_list, [vol.In(SENSOR_TYPES)] - ), - 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.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(CONF_FORECAST): vol.All(cv.ensure_list, [vol.Range(min=0, max=7)]), - vol.Optional(CONF_HOURLY_FORECAST): vol.All( - cv.ensure_list, [vol.Range(min=0, max=48)] - ), - } -) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Dark Sky sensor.""" - latitude = config.get(CONF_LATITUDE, hass.config.latitude) - longitude = config.get(CONF_LONGITUDE, hass.config.longitude) - language = config.get(CONF_LANGUAGE) - interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL) - - if CONF_UNITS in config: - units = config[CONF_UNITS] - elif hass.config.units is METRIC_SYSTEM: - units = "si" - else: - units = "us" - - forecast_data = DarkSkyData( - api_key=config.get(CONF_API_KEY), - latitude=latitude, - longitude=longitude, - units=units, - language=language, - interval=interval, - ) - forecast_data.update() - forecast_data.update_currently() - - # If connection failed don't setup platform. - if forecast_data.data is None: - return - - name = config.get(CONF_NAME) - - forecast = config.get(CONF_FORECAST) - forecast_hour = config.get(CONF_HOURLY_FORECAST) - sensors: list[SensorEntity] = [] - for variable in config[CONF_MONITORED_CONDITIONS]: - if variable in DEPRECATED_SENSOR_TYPES: - _LOGGER.warning("Monitored condition %s is deprecated", variable) - description = SENSOR_TYPES[variable] - if not description.forecast_mode or "currently" in description.forecast_mode: - if variable == "alerts": - sensors.append(DarkSkyAlertSensor(forecast_data, description, name)) - else: - sensors.append(DarkSkySensor(forecast_data, description, name)) - - if forecast is not None and "daily" in description.forecast_mode: - sensors.extend( - [ - DarkSkySensor( - forecast_data, description, name, forecast_day=forecast_day - ) - for forecast_day in forecast - ] - ) - if forecast_hour is not None and "hourly" in description.forecast_mode: - sensors.extend( - [ - DarkSkySensor( - forecast_data, description, name, forecast_hour=forecast_h - ) - for forecast_h in forecast_hour - ] - ) - - add_entities(sensors, True) - - -class DarkSkySensor(SensorEntity): - """Implementation of a Dark Sky sensor.""" - - _attr_attribution = "Powered by Dark Sky" - entity_description: DarkskySensorEntityDescription - - def __init__( - self, - forecast_data, - description: DarkskySensorEntityDescription, - name, - forecast_day=None, - forecast_hour=None, - ) -> None: - """Initialize the sensor.""" - self.entity_description = description - self.forecast_data = forecast_data - self.forecast_day = forecast_day - self.forecast_hour = forecast_hour - self._icon: str | None = None - - if forecast_day is not None: - self._attr_name = f"{name} {description.name} {forecast_day}d" - elif forecast_hour is not None: - self._attr_name = f"{name} {description.name} {forecast_hour}h" - else: - self._attr_name = f"{name} {description.name}" - - @property - def unit_system(self): - """Return the unit system of this entity.""" - return self.forecast_data.unit_system - - @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 - - def update(self) -> None: - """Get the latest data from Dark Sky and updates the states.""" - # Call the API for new forecast data. Each sensor will re-trigger this - # same exact call, but that's fine. We cache results for a short period - # of time to prevent hitting API limits. Note that Dark Sky will - # charge users for too many calls in 1 day, so take care when updating. - self.forecast_data.update() - self.update_unit_of_measurement() - - sensor_type = self.entity_description.key - if sensor_type == "minutely_summary": - self.forecast_data.update_minutely() - minutely = self.forecast_data.data_minutely - self._attr_native_value = getattr(minutely, "summary", "") - self._icon = getattr(minutely, "icon", "") - elif sensor_type == "hourly_summary": - self.forecast_data.update_hourly() - hourly = self.forecast_data.data_hourly - self._attr_native_value = getattr(hourly, "summary", "") - self._icon = getattr(hourly, "icon", "") - elif self.forecast_hour is not None: - self.forecast_data.update_hourly() - hourly = self.forecast_data.data_hourly - if hasattr(hourly, "data"): - self._attr_native_value = self.get_state( - hourly.data[self.forecast_hour] - ) - else: - self._attr_native_value = 0 - elif sensor_type == "daily_summary": - self.forecast_data.update_daily() - daily = self.forecast_data.data_daily - self._attr_native_value = getattr(daily, "summary", "") - self._icon = getattr(daily, "icon", "") - elif self.forecast_day is not None: - self.forecast_data.update_daily() - daily = self.forecast_data.data_daily - if hasattr(daily, "data"): - self._attr_native_value = self.get_state(daily.data[self.forecast_day]) - else: - self._attr_native_value = 0 - else: - self.forecast_data.update_currently() - currently = self.forecast_data.data_currently - self._attr_native_value = self.get_state(currently) - - def get_state(self, data): - """Return a new state based on the type. - - If the sensor type is unknown, the current state is returned. - """ - sensor_type = self.entity_description.key - lookup_type = convert_to_camel(sensor_type) - - if (state := getattr(data, lookup_type, None)) is None: - return None - - if "summary" in sensor_type: - self._icon = getattr(data, "icon", "") - - # Some state data needs to be rounded to whole values or converted to - # percentages - if sensor_type in {"precip_probability", "cloud_cover", "humidity"}: - return round(state * 100, 1) - - if sensor_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", - "precip_accumulation", - "pressure", - "ozone", - "uvIndex", - }: - return round(state, 1) - return state - - -class DarkSkyAlertSensor(SensorEntity): - """Implementation of a Dark Sky sensor.""" - - entity_description: DarkskySensorEntityDescription - _attr_native_value: int | None - - def __init__( - self, forecast_data, description: DarkskySensorEntityDescription, name - ) -> None: - """Initialize the sensor.""" - self.entity_description = description - self.forecast_data = forecast_data - self._alerts = None - - self._attr_name = f"{name} {description.name}" - - @property - def icon(self): - """Icon to use in the frontend, if any.""" - if self._attr_native_value is not None and self._attr_native_value > 0: - return "mdi:alert-circle" - return "mdi:alert-circle-outline" - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - return self._alerts - - def update(self) -> None: - """Get the latest data from Dark Sky and updates the states.""" - # Call the API for new forecast data. Each sensor will re-trigger this - # same exact call, but that's fine. We cache results for a short period - # of time to prevent hitting API limits. Note that Dark Sky will - # charge users for too many calls in 1 day, so take care when updating. - self.forecast_data.update() - self.forecast_data.update_alerts() - alerts = self.forecast_data.data_alerts - self._attr_native_value = self.get_state(alerts) - - def get_state(self, data): - """Return a new state based on the type. - - If the sensor type is unknown, the current state is returned. - """ - 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 - alerts[dkey] = getattr(alert, attr) - self._alerts = alerts - - return len(data) - - -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}" - - -class DarkSkyData: - """Get the latest data from Darksky.""" - - def __init__(self, api_key, latitude, longitude, units, language, interval): - """Initialize the data object.""" - self._api_key = api_key - self.latitude = latitude - self.longitude = longitude - self.units = units - self.language = language - self._connect_error = False - - self.data = None - self.unit_system = None - self.data_currently = None - self.data_minutely = None - self.data_hourly = None - self.data_daily = None - self.data_alerts = None - - # Apply throttling to methods using configured interval - self.update = Throttle(interval)(self._update) - self.update_currently = Throttle(interval)(self._update_currently) - self.update_minutely = Throttle(interval)(self._update_minutely) - self.update_hourly = Throttle(interval)(self._update_hourly) - self.update_daily = Throttle(interval)(self._update_daily) - self.update_alerts = Throttle(interval)(self._update_alerts) - - def _update(self): - """Get the latest data from Dark Sky.""" - try: - self.data = forecastio.load_forecast( - self._api_key, - self.latitude, - self.longitude, - units=self.units, - lang=self.language, - ) - if self._connect_error: - self._connect_error = False - _LOGGER.info("Reconnected to Dark Sky") - except (ConnectError, HTTPError, Timeout, ValueError) as error: - if not self._connect_error: - self._connect_error = True - _LOGGER.error("Unable to connect to Dark Sky: %s", error) - self.data = None - self.unit_system = self.data and self.data.json["flags"]["units"] - - def _update_currently(self): - """Update currently data.""" - self.data_currently = self.data and self.data.currently() - - def _update_minutely(self): - """Update minutely data.""" - self.data_minutely = self.data and self.data.minutely() - - def _update_hourly(self): - """Update hourly data.""" - self.data_hourly = self.data and self.data.hourly() - - def _update_daily(self): - """Update daily data.""" - self.data_daily = self.data and self.data.daily() - - def _update_alerts(self): - """Update alerts data.""" - self.data_alerts = self.data and self.data.alerts() diff --git a/homeassistant/components/darksky/weather.py b/homeassistant/components/darksky/weather.py deleted file mode 100644 index 25672908670..00000000000 --- a/homeassistant/components/darksky/weather.py +++ /dev/null @@ -1,281 +0,0 @@ -"""Support for retrieving meteorological data from Dark Sky.""" -from __future__ import annotations - -from datetime import timedelta -import logging - -import forecastio -from requests.exceptions import ConnectionError as ConnectError, HTTPError, Timeout -import voluptuous as vol - -from homeassistant.components.weather import ( - ATTR_CONDITION_CLEAR_NIGHT, - ATTR_CONDITION_CLOUDY, - 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, - ATTR_FORECAST_CONDITION, - ATTR_FORECAST_NATIVE_PRECIPITATION, - ATTR_FORECAST_NATIVE_TEMP, - ATTR_FORECAST_NATIVE_TEMP_LOW, - ATTR_FORECAST_NATIVE_WIND_SPEED, - ATTR_FORECAST_TIME, - ATTR_FORECAST_WIND_BEARING, - PLATFORM_SCHEMA, - WeatherEntity, -) -from homeassistant.const import ( - CONF_API_KEY, - CONF_LATITUDE, - CONF_LONGITUDE, - CONF_MODE, - CONF_NAME, - UnitOfLength, - UnitOfPrecipitationDepth, - UnitOfPressure, - UnitOfSpeed, - UnitOfTemperature, -) -from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity_platform import AddEntitiesCallback -from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from homeassistant.util import Throttle -from homeassistant.util.dt import utc_from_timestamp - -_LOGGER = logging.getLogger(__name__) - -ATTRIBUTION = "Powered by Dark Sky" - -FORECAST_MODE = ["hourly", "daily"] - -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": None, -} - -CONF_UNITS = "units" - -DEFAULT_NAME = "Dark Sky" - -PLATFORM_SCHEMA = vol.All( - cv.removed(CONF_UNITS), - PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_API_KEY): cv.string, - vol.Optional(CONF_LATITUDE): cv.latitude, - vol.Optional(CONF_LONGITUDE): cv.longitude, - vol.Optional(CONF_MODE, default="hourly"): vol.In(FORECAST_MODE), - vol.Optional(CONF_UNITS): vol.In(["auto", "si", "us", "ca", "uk", "uk2"]), - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - } - ), -) - -MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=3) - - -def setup_platform( - hass: HomeAssistant, - config: ConfigType, - add_entities: AddEntitiesCallback, - discovery_info: DiscoveryInfoType | None = None, -) -> None: - """Set up the Dark Sky weather.""" - latitude = config.get(CONF_LATITUDE, hass.config.latitude) - longitude = config.get(CONF_LONGITUDE, hass.config.longitude) - name = config.get(CONF_NAME) - mode = config.get(CONF_MODE) - - units = "si" - dark_sky = DarkSkyData(config.get(CONF_API_KEY), latitude, longitude, units) - - add_entities([DarkSkyWeather(name, dark_sky, mode)], True) - - -class DarkSkyWeather(WeatherEntity): - """Representation of a weather condition.""" - - _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS - _attr_native_pressure_unit = UnitOfPressure.MBAR - _attr_native_temperature_unit = UnitOfTemperature.CELSIUS - _attr_native_visibility_unit = UnitOfLength.KILOMETERS - _attr_native_wind_speed_unit = UnitOfSpeed.METERS_PER_SECOND - - def __init__(self, name, dark_sky, mode): - """Initialize Dark Sky weather.""" - self._name = name - self._dark_sky = dark_sky - self._mode = mode - - self._ds_data = None - self._ds_currently = None - self._ds_hourly = None - self._ds_daily = None - - @property - def available(self) -> bool: - """Return if weather data is available from Dark Sky.""" - return self._ds_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.""" - return self._ds_currently.get("temperature") - - @property - def humidity(self): - """Return the humidity.""" - return round(self._ds_currently.get("humidity") * 100.0, 2) - - @property - def native_wind_speed(self): - """Return the wind speed.""" - return self._ds_currently.get("windSpeed") - - @property - def wind_bearing(self): - """Return the wind bearing.""" - return self._ds_currently.get("windBearing") - - @property - def ozone(self): - """Return the ozone level.""" - return self._ds_currently.get("ozone") - - @property - def native_pressure(self): - """Return the pressure.""" - return self._ds_currently.get("pressure") - - @property - def native_visibility(self): - """Return the visibility.""" - return self._ds_currently.get("visibility") - - @property - def condition(self): - """Return the weather condition.""" - return MAP_CONDITION.get(self._ds_currently.get("icon")) - - @property - def forecast(self): - """Return the forecast array.""" - - # Per conversation with Joshua Reyes of Dark Sky, to get the total - # forecasted precipitation, you have to multiple the intensity by - # the hours for the forecast interval - def calc_precipitation(intensity, hours): - amount = None - if intensity is not None: - amount = round((intensity * hours), 1) - return amount if amount > 0 else None - - data = None - - if self._mode == "daily": - data = [ - { - ATTR_FORECAST_TIME: utc_from_timestamp( - entry.d.get("time") - ).isoformat(), - ATTR_FORECAST_NATIVE_TEMP: entry.d.get("temperatureHigh"), - ATTR_FORECAST_NATIVE_TEMP_LOW: entry.d.get("temperatureLow"), - ATTR_FORECAST_NATIVE_PRECIPITATION: calc_precipitation( - entry.d.get("precipIntensity"), 24 - ), - ATTR_FORECAST_NATIVE_WIND_SPEED: entry.d.get("windSpeed"), - ATTR_FORECAST_WIND_BEARING: entry.d.get("windBearing"), - ATTR_FORECAST_CONDITION: MAP_CONDITION.get(entry.d.get("icon")), - } - for entry in self._ds_daily.data - ] - else: - data = [ - { - ATTR_FORECAST_TIME: utc_from_timestamp( - entry.d.get("time") - ).isoformat(), - ATTR_FORECAST_NATIVE_TEMP: entry.d.get("temperature"), - ATTR_FORECAST_NATIVE_PRECIPITATION: calc_precipitation( - entry.d.get("precipIntensity"), 1 - ), - ATTR_FORECAST_CONDITION: MAP_CONDITION.get(entry.d.get("icon")), - } - for entry in self._ds_hourly.data - ] - - return data - - def update(self) -> None: - """Get the latest data from Dark Sky.""" - self._dark_sky.update() - - self._ds_data = self._dark_sky.data - currently = self._dark_sky.currently - self._ds_currently = currently.d if currently else {} - self._ds_hourly = self._dark_sky.hourly - self._ds_daily = self._dark_sky.daily - - -class DarkSkyData: - """Get the latest data from Dark Sky.""" - - def __init__(self, api_key, latitude, longitude, units): - """Initialize the data object.""" - self._api_key = api_key - self.latitude = latitude - self.longitude = longitude - self.requested_units = units - - self.data = None - self.currently = None - self.hourly = None - self.daily = None - self._connect_error = False - - @Throttle(MIN_TIME_BETWEEN_UPDATES) - def update(self): - """Get the latest data from Dark Sky.""" - try: - self.data = forecastio.load_forecast( - self._api_key, self.latitude, self.longitude, units=self.requested_units - ) - self.currently = self.data.currently() - self.hourly = self.data.hourly() - self.daily = self.data.daily() - if self._connect_error: - self._connect_error = False - _LOGGER.info("Reconnected to Dark Sky") - except (ConnectError, HTTPError, Timeout, ValueError) as error: - if not self._connect_error: - self._connect_error = True - _LOGGER.error("Unable to connect to Dark Sky. %s", error) - self.data = None diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 311f8414f9a..066dff4a5a8 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -926,12 +926,6 @@ "config_flow": false, "iot_class": "local_polling" }, - "darksky": { - "name": "Dark Sky", - "integration_type": "hub", - "config_flow": false, - "iot_class": "cloud_polling" - }, "datadog": { "name": "Datadog", "integration_type": "hub", diff --git a/requirements_all.txt b/requirements_all.txt index 4b60930b9e2..82d31f7f41a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2041,9 +2041,6 @@ python-etherscan-api==0.0.3 # homeassistant.components.familyhub python-family-hub-local==0.0.2 -# homeassistant.components.darksky -python-forecastio==1.4.0 - # homeassistant.components.fully_kiosk python-fullykiosk==0.0.12 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4d983d61f55..94698ec4c18 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1473,9 +1473,6 @@ python-bsblan==0.5.11 # homeassistant.components.ecobee python-ecobee-api==0.2.14 -# homeassistant.components.darksky -python-forecastio==1.4.0 - # homeassistant.components.fully_kiosk python-fullykiosk==0.0.12 diff --git a/tests/components/darksky/__init__.py b/tests/components/darksky/__init__.py deleted file mode 100644 index b58d250e975..00000000000 --- a/tests/components/darksky/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the darksky component.""" diff --git a/tests/components/darksky/test_sensor.py b/tests/components/darksky/test_sensor.py deleted file mode 100644 index bd6552048e9..00000000000 --- a/tests/components/darksky/test_sensor.py +++ /dev/null @@ -1,171 +0,0 @@ -"""The tests for the Dark Sky platform.""" -from datetime import timedelta -import re -from unittest.mock import patch - -import forecastio -from requests.exceptions import ConnectionError as ConnectError -import requests_mock - -from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component - -from tests.common import load_fixture - -VALID_CONFIG_MINIMAL = { - "sensor": { - "platform": "darksky", - "api_key": "foo", - "forecast": [1, 2], - "hourly_forecast": [1, 2], - "monitored_conditions": ["summary", "icon", "temperature_high", "alerts"], - "scan_interval": timedelta(seconds=120), - } -} - -INVALID_CONFIG_MINIMAL = { - "sensor": { - "platform": "darksky", - "api_key": "foo", - "forecast": [1, 2], - "hourly_forecast": [1, 2], - "monitored_conditions": ["summary", "iocn", "temperature_high"], - "scan_interval": timedelta(seconds=120), - } -} - -VALID_CONFIG_LANG_DE = { - "sensor": { - "platform": "darksky", - "api_key": "foo", - "forecast": [1, 2], - "hourly_forecast": [1, 2], - "units": "us", - "language": "de", - "monitored_conditions": [ - "summary", - "icon", - "temperature_high", - "minutely_summary", - "hourly_summary", - "daily_summary", - "humidity", - "alerts", - ], - "scan_interval": timedelta(seconds=120), - } -} - -INVALID_CONFIG_LANG = { - "sensor": { - "platform": "darksky", - "api_key": "foo", - "forecast": [1, 2], - "hourly_forecast": [1, 2], - "language": "yz", - "monitored_conditions": ["summary", "icon", "temperature_high"], - "scan_interval": timedelta(seconds=120), - } -} - - -async def test_setup_with_config( - hass: HomeAssistant, requests_mock: requests_mock.Mocker -) -> None: - """Test the platform setup with configuration.""" - with patch("homeassistant.components.darksky.sensor.forecastio.load_forecast"): - assert await async_setup_component(hass, "sensor", VALID_CONFIG_MINIMAL) - await hass.async_block_till_done() - - state = hass.states.get("sensor.dark_sky_summary") - assert state is not None - - -async def test_setup_with_invalid_config(hass: HomeAssistant) -> None: - """Test the platform setup with invalid configuration.""" - assert await async_setup_component(hass, "sensor", INVALID_CONFIG_MINIMAL) - await hass.async_block_till_done() - - state = hass.states.get("sensor.dark_sky_summary") - assert state is None - - -async def test_setup_with_language_config(hass: HomeAssistant) -> None: - """Test the platform setup with language configuration.""" - with patch("homeassistant.components.darksky.sensor.forecastio.load_forecast"): - assert await async_setup_component(hass, "sensor", VALID_CONFIG_LANG_DE) - await hass.async_block_till_done() - - state = hass.states.get("sensor.dark_sky_summary") - assert state is not None - - -async def test_setup_with_invalid_language_config(hass: HomeAssistant) -> None: - """Test the platform setup with language configuration.""" - assert await async_setup_component(hass, "sensor", INVALID_CONFIG_LANG) - await hass.async_block_till_done() - - state = hass.states.get("sensor.dark_sky_summary") - assert state is None - - -async def test_setup_bad_api_key( - hass: HomeAssistant, requests_mock: requests_mock.Mocker -) -> None: - """Test for handling a bad API key.""" - # The Dark Sky API wrapper that we use raises an HTTP error - # when you try to use a bad (or no) API key. - url = "https://api.darksky.net/forecast/{}/{},{}?units=auto".format( - "foo", str(hass.config.latitude), str(hass.config.longitude) - ) - msg = f"400 Client Error: Bad Request for url: {url}" - requests_mock.get(url, text=msg, status_code=400) - - assert await async_setup_component( - hass, "sensor", {"sensor": {"platform": "darksky", "api_key": "foo"}} - ) - await hass.async_block_till_done() - - assert hass.states.get("sensor.dark_sky_summary") is None - - -async def test_connection_error(hass: HomeAssistant) -> None: - """Test setting up with a connection error.""" - with patch( - "homeassistant.components.darksky.sensor.forecastio.load_forecast", - side_effect=ConnectError(), - ): - await async_setup_component(hass, "sensor", VALID_CONFIG_MINIMAL) - await hass.async_block_till_done() - - state = hass.states.get("sensor.dark_sky_summary") - assert state is None - - -async def test_setup(hass: HomeAssistant, requests_mock: requests_mock.Mocker) -> None: - """Test for successfully setting up the forecast.io platform.""" - with patch( - "forecastio.api.get_forecast", wraps=forecastio.api.get_forecast - ) as mock_get_forecast: - uri = ( - r"https://api.(darksky.net|forecast.io)\/forecast\/(\w+)\/" - r"(-?\d+\.?\d*),(-?\d+\.?\d*)" - ) - requests_mock.get(re.compile(uri), text=load_fixture("darksky.json")) - - assert await async_setup_component(hass, "sensor", VALID_CONFIG_MINIMAL) - await hass.async_block_till_done() - - assert mock_get_forecast.call_count == 1 - assert len(hass.states.async_entity_ids()) == 13 - - state = hass.states.get("sensor.dark_sky_summary") - assert state is not None - assert state.state == "Clear" - assert state.attributes.get("friendly_name") == "Dark Sky Summary" - state = hass.states.get("sensor.dark_sky_alerts") - assert state.state == "2" - - state = hass.states.get("sensor.dark_sky_daytime_high_temperature_1d") - assert state is not None - assert state.attributes.get("device_class") == "temperature" diff --git a/tests/components/darksky/test_weather.py b/tests/components/darksky/test_weather.py deleted file mode 100644 index 5b0c67916cd..00000000000 --- a/tests/components/darksky/test_weather.py +++ /dev/null @@ -1,52 +0,0 @@ -"""The tests for the Dark Sky weather component.""" -import re -from unittest.mock import patch - -import forecastio -from requests.exceptions import ConnectionError as ConnectError -import requests_mock - -from homeassistant.components import weather -from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component - -from tests.common import load_fixture - - -async def test_setup(hass: HomeAssistant, requests_mock: requests_mock.Mocker) -> None: - """Test for successfully setting up the forecast.io platform.""" - with patch( - "forecastio.api.get_forecast", wraps=forecastio.api.get_forecast - ) as mock_get_forecast: - requests_mock.get( - re.compile( - r"https://api.(darksky.net|forecast.io)\/forecast\/(\w+)\/" - r"(-?\d+\.?\d*),(-?\d+\.?\d*)" - ), - text=load_fixture("darksky.json"), - ) - - assert await async_setup_component( - hass, - weather.DOMAIN, - {"weather": {"name": "test", "platform": "darksky", "api_key": "foo"}}, - ) - await hass.async_block_till_done() - - assert mock_get_forecast.call_count == 1 - state = hass.states.get("weather.test") - assert state.state == "sunny" - - -async def test_failed_setup(hass: HomeAssistant) -> None: - """Test to ensure that a network error does not break component state.""" - with patch("forecastio.load_forecast", side_effect=ConnectError()): - assert await async_setup_component( - hass, - weather.DOMAIN, - {"weather": {"name": "test", "platform": "darksky", "api_key": "foo"}}, - ) - await hass.async_block_till_done() - - state = hass.states.get("weather.test") - assert state.state == "unavailable"