Switch to xmltodict and pass over missing temperature (fixes #2433) (#2463)

* Switch to xmltodict and pass over missing temperature (fixes #2433)

* Add guard clauses
This commit is contained in:
Fabian Affolter 2016-07-14 03:30:11 +02:00 committed by Paulus Schoutsen
parent 675283c23e
commit a0c1c918b8
3 changed files with 65 additions and 50 deletions

View file

@ -5,18 +5,18 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.swiss_hydrological_data/
"""
import logging
import collections
from datetime import timedelta
import voluptuous as vol
import requests
from homeassistant.const import (TEMP_CELSIUS, CONF_PLATFORM, CONF_NAME)
from homeassistant.const import (TEMP_CELSIUS, CONF_PLATFORM, CONF_NAME,
STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle
REQUIREMENTS = ['beautifulsoup4==4.4.1']
REQUIREMENTS = ['xmltodict==0.10.2']
_LOGGER = logging.getLogger(__name__)
_RESOURCE = 'http://www.hydrodata.ch/xml/SMS.xml'
@ -39,33 +39,25 @@ ATTR_TEMPERATURE_MAX = 'Temperature max'
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): 'swiss_hydrological_data',
vol.Optional(CONF_NAME): cv.string,
vol.Required(CONF_STATION): cv.string,
vol.Required(CONF_STATION): vol.Coerce(int),
})
HydroData = collections.namedtuple(
"HydrologicalData",
['waterlevel', 'waterlevel_max', 'waterlevel_mean', 'temperature',
'temperature_max', 'temperature_mean', 'discharge', 'discharge_max',
'discharge_mean', 'location', 'update_time'])
# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Swiss hydrological sensor."""
from bs4 import BeautifulSoup
import xmltodict
station = config.get(CONF_STATION)
name = config.get(CONF_NAME, DEFAULT_NAME)
try:
response = requests.get(_RESOURCE, timeout=5)
if BeautifulSoup(
response.content,
'html.parser').find(strnr='{}'.format(station)) is None:
_LOGGER.error('The given station does not seem to exist: %s',
station)
if any(str(station) == location.get('@StrNr') for location in
xmltodict.parse(response.text)['AKT_Data']['MesPar']) is False:
_LOGGER.error('The given station does not exist: %s', station)
return False
except requests.exceptions.ConnectionError:
_LOGGER.error('The URL is not accessible')
@ -94,29 +86,47 @@ class SwissHydrologicalDataSensor(Entity):
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit_of_measurement
if self._state is not STATE_UNKNOWN:
return self._unit_of_measurement
else:
return None
@property
def state(self):
"""Return the state of the sensor."""
return round(float(self._state), 1)
try:
return round(float(self._state), 1)
except ValueError:
return STATE_UNKNOWN
@property
def device_state_attributes(self):
"""Return the state attributes."""
attributes = {}
if self.data.measurings is not None:
return {
ATTR_LOCATION: self.data.measurings.location,
ATTR_UPDATE: self.data.measurings.update_time,
ATTR_DISCHARGE: self.data.measurings.discharge,
ATTR_WATERLEVEL: self.data.measurings.waterlevel,
ATTR_DISCHARGE_MEAN: self.data.measurings.discharge_mean,
ATTR_WATERLEVEL_MEAN: self.data.measurings.waterlevel_mean,
ATTR_TEMPERATURE_MEAN: self.data.measurings.temperature_mean,
ATTR_DISCHARGE_MAX: self.data.measurings.discharge_max,
ATTR_WATERLEVEL_MAX: self.data.measurings.waterlevel_max,
ATTR_TEMPERATURE_MAX: self.data.measurings.temperature_max,
}
if '02' in self.data.measurings:
attributes[ATTR_WATERLEVEL] = self.data.measurings['02'][
'current']
attributes[ATTR_WATERLEVEL_MEAN] = self.data.measurings['02'][
'mean']
attributes[ATTR_WATERLEVEL_MAX] = self.data.measurings['02'][
'max']
if '03' in self.data.measurings:
attributes[ATTR_TEMPERATURE_MEAN] = self.data.measurings['03'][
'mean']
attributes[ATTR_TEMPERATURE_MAX] = self.data.measurings['03'][
'max']
if '10' in self.data.measurings:
attributes[ATTR_DISCHARGE] = self.data.measurings['10'][
'current']
attributes[ATTR_DISCHARGE_MEAN] = self.data.measurings['10'][
'current']
attributes[ATTR_DISCHARGE_MAX] = self.data.measurings['10'][
'max']
attributes[ATTR_LOCATION] = self.data.measurings['location']
attributes[ATTR_UPDATE] = self.data.measurings['update_time']
return attributes
@property
def icon(self):
@ -128,7 +138,10 @@ class SwissHydrologicalDataSensor(Entity):
"""Get the latest data and update the states."""
self.data.update()
if self.data.measurings is not None:
self._state = self.data.measurings.temperature
if '03' not in self.data.measurings:
self._state = STATE_UNKNOWN
else:
self._state = self.data.measurings['03']['current']
# pylint: disable=too-few-public-methods
@ -143,28 +156,33 @@ class HydrologicalData(object):
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Get the latest data from hydrodata.ch."""
from bs4 import BeautifulSoup
import xmltodict
details = {}
try:
response = requests.get(_RESOURCE, timeout=5)
except requests.exceptions.ConnectionError:
_LOGGER.error('Unable to retrieve data from %s', _RESOURCE)
try:
soup = BeautifulSoup(response.content, 'html.parser')
stations = xmltodict.parse(response.text)['AKT_Data']['MesPar']
# Water level: Typ="02", temperature: Typ="03", discharge: Typ="10"
type02, type03, type10 = [
soup.find(strnr='{}'.format(self.station), typ='{}'.format(i))
for i in ['02', '03', '10']]
for station in stations:
if str(self.station) != station.get('@StrNr'):
continue
for data in ['02', '03', '10']:
if data != station.get('@Typ'):
continue
values = station.get('Wert')
if values is not None:
details[data] = {
'current': values[0],
'max': list(values[4].items())[1][1],
'mean': list(values[3].items())[1][1]}
details = []
for entry in [type02, type03, type10]:
details.append(entry.wert.string)
details.append(entry.find(typ="max24").string)
details.append(entry.find(typ="m24").string)
details.append(type03.find('name').string)
details.append(type03.find('zeit').string)
details['location'] = station.get('Name')
details['update_time'] = station.get('Zeit')
self.measurings = HydroData._make(details)
self.measurings = details
except AttributeError:
self.measurings = None

View file

@ -18,8 +18,7 @@ from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['xmltodict']
REQUIREMENTS = ['xmltodict==0.10.2']
# Sensor types are defined like so:
SENSOR_TYPES = {

View file

@ -30,9 +30,6 @@ apcaccess==0.0.4
# homeassistant.components.sun
astral==1.2
# homeassistant.components.sensor.swiss_hydrological_data
beautifulsoup4==4.4.1
# homeassistant.components.light.blinksticklight
blinkstick==1.1.7
@ -440,8 +437,9 @@ websocket-client==0.37.0
# homeassistant.components.zigbee
xbee-helper==0.0.7
# homeassistant.components.sensor.swiss_hydrological_data
# homeassistant.components.sensor.yr
xmltodict
xmltodict==0.10.2
# homeassistant.components.sensor.yweather
yahooweather==0.4