""" homeassistant.components.sensor.forecast ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Forecast.io weather service. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.forecast/ """ import logging from datetime import timedelta from homeassistant.util import Throttle from homeassistant.const import (CONF_API_KEY, TEMP_CELCIUS) from homeassistant.helpers.entity import Entity REQUIREMENTS = ['python-forecastio==1.3.3'] _LOGGER = logging.getLogger(__name__) # Sensor types are defined like so: # Name, si unit, us unit, ca unit, uk unit, uk2 unit SENSOR_TYPES = { 'summary': ['Summary', None, None, None, None, None], 'icon': ['Icon', None, None, None, None, None], 'nearest_storm_distance': ['Nearest Storm Distance', 'km', 'm', 'km', 'km', 'm'], 'nearest_storm_bearing': ['Nearest Storm Bearing', '°', '°', '°', '°', '°'], 'precip_type': ['Precip', None, None, None, None, None], 'precip_intensity': ['Precip Intensity', 'mm', 'in', 'mm', 'mm', 'mm'], 'precip_probability': ['Precip Probability', '%', '%', '%', '%', '%'], 'temperature': ['Temperature', '°C', '°F', '°C', '°C', '°C'], 'apparent_temperature': ['Apparent Temperature', '°C', '°F', '°C', '°C', '°C'], 'dew_point': ['Dew point', '°C', '°F', '°C', '°C', '°C'], 'wind_speed': ['Wind Speed', 'm/s', 'mph', 'km/h', 'mph', 'mph'], 'wind_bearing': ['Wind Bearing', '°', '°', '°', '°', '°'], 'cloud_cover': ['Cloud Coverage', '%', '%', '%', '%', '%'], 'humidity': ['Humidity', '%', '%', '%', '%', '%'], 'pressure': ['Pressure', 'mBar', 'mBar', 'mBar', 'mBar', 'mBar'], 'visibility': ['Visibility', 'km', 'm', 'km', 'km', 'm'], 'ozone': ['Ozone', 'DU', 'DU', 'DU', 'DU', 'DU'], } # Return cached results if last scan was less then this time ago MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120) def setup_platform(hass, config, add_devices, discovery_info=None): """ Get the Forecast.io sensor. """ import forecastio if None in (hass.config.latitude, hass.config.longitude): _LOGGER.error("Latitude or longitude not set in Home Assistant config") return False try: forecast = forecastio.load_forecast(config.get(CONF_API_KEY, None), hass.config.latitude, hass.config.longitude) forecast.currently() except ValueError: _LOGGER.error( "Connection error " "Please check your settings for Forecast.io.") return False if 'units' in config: units = config['units'] elif hass.config.temperature_unit == TEMP_CELCIUS: units = 'si' else: units = 'us' data = ForeCastData(config.get(CONF_API_KEY, None), hass.config.latitude, hass.config.longitude, units) dev = [] for variable in config['monitored_conditions']: if variable not in SENSOR_TYPES: _LOGGER.error('Sensor type: "%s" does not exist', variable) else: dev.append(ForeCastSensor(data, variable)) add_devices(dev) # pylint: disable=too-few-public-methods class ForeCastSensor(Entity): """ Implements an Forecast.io sensor. """ def __init__(self, weather_data, sensor_type): self.client_name = 'Weather' self._name = SENSOR_TYPES[sensor_type][0] self.forecast_client = weather_data self.type = sensor_type self._state = None self._unit_system = self.forecast_client.unit_system if self._unit_system == 'si': self._unit_of_measurement = SENSOR_TYPES[self.type][1] elif self._unit_system == 'us': self._unit_of_measurement = SENSOR_TYPES[self.type][2] elif self._unit_system == 'ca': self._unit_of_measurement = SENSOR_TYPES[self.type][3] elif self._unit_system == 'uk': self._unit_of_measurement = SENSOR_TYPES[self.type][4] elif self._unit_system == 'uk2': self._unit_of_measurement = SENSOR_TYPES[self.type][5] self.update() @property def name(self): return '{} {}'.format(self.client_name, self._name) @property def state(self): """ Returns the state of the device. """ return self._state @property def unit_of_measurement(self): """ Unit of measurement of this entity, if any. """ return self._unit_of_measurement @property def unit_system(self): """ Unit system of this entity. """ return self._unit_system # pylint: disable=too-many-branches def update(self): """ Gets the latest data from Forecast.io and updates the states. """ import forecastio self.forecast_client.update() data = self.forecast_client.data try: if self.type == 'summary': self._state = data.summary elif self.type == 'icon': self._state = data.icon elif self.type == 'nearest_storm_distance': self._state = data.nearestStormDistance elif self.type == 'nearest_storm_bearing': self._state = data.nearestStormBearing elif self.type == 'precip_intensity': self._state = data.precipIntensity elif self.type == 'precip_type': self._state = data.precipType elif self.type == 'precip_probability': self._state = round(data.precipProbability * 100, 1) elif self.type == 'dew_point': self._state = round(data.dewPoint, 1) elif self.type == 'temperature': self._state = round(data.temperature, 1) elif self.type == 'apparent_temperature': self._state = round(data.apparentTemperature, 1) elif self.type == 'wind_speed': self._state = data.windSpeed elif self.type == 'wind_bearing': self._state = data.windBearing elif self.type == 'cloud_cover': self._state = round(data.cloudCover * 100, 1) elif self.type == 'humidity': self._state = round(data.humidity * 100, 1) elif self.type == 'pressure': self._state = round(data.pressure, 1) elif self.type == 'visibility': self._state = data.visibility elif self.type == 'ozone': self._state = round(data.ozone, 1) except forecastio.utils.PropertyUnavailable: pass class ForeCastData(object): """ Gets the latest data from Forecast.io. """ def __init__(self, api_key, latitude, longitude, units): self._api_key = api_key self.latitude = latitude self.longitude = longitude self.data = None self.unit_system = None self.units = units self.update() @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """ Gets the latest data from Forecast.io. """ import forecastio forecast = forecastio.load_forecast(self._api_key, self.latitude, self.longitude, units=self.units) self.data = forecast.currently() self.unit_system = forecast.json['flags']['units']