""" Support for the Yahoo! Weather service. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.yweather/ """ import logging from datetime import timedelta import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( TEMP_CELSIUS, CONF_MONITORED_CONDITIONS, STATE_UNKNOWN) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle REQUIREMENTS = ["yahooweather==0.7"] _LOGGER = logging.getLogger(__name__) CONF_FORECAST = 'forecast' CONF_WOEID = 'woeid' MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=120) SENSOR_TYPES = { 'weather_current': ['Current', None], 'weather': ['Condition', None], 'temperature': ['Temperature', "temperature"], 'temp_min': ['Temperature', "temperature"], 'temp_max': ['Temperature', "temperature"], 'wind_speed': ['Wind speed', "speed"], 'humidity': ['Humidity', "%"], 'pressure': ['Pressure', "pressure"], 'visibility': ['Visibility', "distance"], } PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_WOEID, default=None): cv.string, vol.Optional(CONF_FORECAST, default=0): vol.All(vol.Coerce(int), vol.Range(min=0, max=5)), vol.Required(CONF_MONITORED_CONDITIONS, default=[]): [vol.In(SENSOR_TYPES)], }) def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the Yahoo! weather sensor.""" from yahooweather import get_woeid, UNIT_C, UNIT_F unit = hass.config.units.temperature_unit woeid = config.get(CONF_WOEID) forecast = config.get(CONF_FORECAST) # convert unit yunit = UNIT_C if unit == TEMP_CELSIUS else UNIT_F # for print HA style temp SENSOR_TYPES["temperature"][1] = unit SENSOR_TYPES["temp_min"][1] = unit SENSOR_TYPES["temp_max"][1] = unit # if not exists a customer woeid / calc from HA if woeid is None: woeid = get_woeid(hass.config.latitude, hass.config.longitude) # receive a error? if woeid is None: _LOGGER.critical("Can't retrieve WOEID from yahoo!") return False # create api object yahoo_api = YahooWeatherData(woeid, yunit) # if update is false, it will never work... if not yahoo_api.update(): _LOGGER.critical("Can't retrieve weather data from yahoo!") return False # check if forecast support by API if forecast >= len(yahoo_api.yahoo.Forecast): _LOGGER.error("Yahoo! only support %d days forcast!", len(yahoo_api.yahoo.Forecast)) return False dev = [] for variable in config[CONF_MONITORED_CONDITIONS]: dev.append(YahooWeatherSensor(yahoo_api, forecast, variable)) add_devices(dev) # pylint: disable=too-many-instance-attributes class YahooWeatherSensor(Entity): """Implementation of an Yahoo! weather sensor.""" def __init__(self, weather_data, forecast, sensor_type): """Initialize the sensor.""" self._client = 'Weather' self._name = SENSOR_TYPES[sensor_type][0] self._type = sensor_type self._state = STATE_UNKNOWN self._unit = SENSOR_TYPES[sensor_type][1] self._data = weather_data self._forecast = forecast self._code = None # update data self.update() @property def name(self): """Return the name of the sensor.""" return '{} {}'.format(self._client, self._name) @property def state(self): """Return the state of the device.""" return self._state @property def unit_of_measurement(self): """Return the unit of measurement of this entity, if any.""" return self._data.yahoo.Units.get(self._unit, self._unit) @property def entity_picture(self): """Return the entity picture to use in the frontend, if any.""" if self._code is None or "weather" not in self._type: return None return self._data.yahoo.getWeatherImage(self._code) @property def device_state_attributes(self): """Return the state attributes.""" return { 'about': "Weather forecast delivered by Yahoo! Inc. are provided" " free of charge for use by individuals and non-profit" " organizations for personal, non-commercial uses." } def update(self): """Get the latest data from Yahoo! and updates the states.""" self._data.update() if not self._data.yahoo.RawData: _LOGGER.info("Don't receive weather data from yahoo!") return # default code for weather image self._code = self._data.yahoo.Now["code"] # read data if self._type == "weather_current": self._state = self._data.yahoo.Now["text"] elif self._type == "weather": self._code = self._data.yahoo.Forecast[self._forecast]["code"] self._state = self._data.yahoo.Forecast[self._forecast]["text"] elif self._type == "temperature": self._state = self._data.yahoo.Now["temp"] elif self._type == "temp_min": self._code = self._data.yahoo.Forecast[self._forecast]["code"] self._state = self._data.yahoo.Forecast[self._forecast]["low"] elif self._type == "temp_max": self._code = self._data.yahoo.Forecast[self._forecast]["code"] self._state = self._data.yahoo.Forecast[self._forecast]["high"] elif self._type == "wind_speed": self._state = self._data.yahoo.Wind["speed"] elif self._type == "humidity": self._state = self._data.yahoo.Atmosphere["humidity"] elif self._type == "pressure": self._state = self._data.yahoo.Atmosphere["pressure"] elif self._type == "visibility": self._state = self._data.yahoo.Atmosphere["visibility"] # pylint: disable=too-few-public-methods class YahooWeatherData(object): """Handle yahoo api object and limit updates.""" def __init__(self, woeid, temp_unit): """Initialize the data object.""" from yahooweather import YahooWeather # init yahoo api object self._yahoo = YahooWeather(woeid, temp_unit) @property def yahoo(self): """Return yahoo api object.""" return self._yahoo @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Get the latest data from yahoo. True is success.""" return self._yahoo.updateWeather()