* Switch to xmltodict and pass over missing temperature (fixes #2433) * Add guard clauses
This commit is contained in:
parent
675283c23e
commit
a0c1c918b8
3 changed files with 65 additions and 50 deletions
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue