* 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/
|
https://home-assistant.io/components/sensor.swiss_hydrological_data/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import collections
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import requests
|
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
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
REQUIREMENTS = ['beautifulsoup4==4.4.1']
|
REQUIREMENTS = ['xmltodict==0.10.2']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_RESOURCE = 'http://www.hydrodata.ch/xml/SMS.xml'
|
_RESOURCE = 'http://www.hydrodata.ch/xml/SMS.xml'
|
||||||
|
@ -39,33 +39,25 @@ ATTR_TEMPERATURE_MAX = 'Temperature max'
|
||||||
PLATFORM_SCHEMA = vol.Schema({
|
PLATFORM_SCHEMA = vol.Schema({
|
||||||
vol.Required(CONF_PLATFORM): 'swiss_hydrological_data',
|
vol.Required(CONF_PLATFORM): 'swiss_hydrological_data',
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
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.
|
# Return cached results if last scan was less then this time ago.
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup the Swiss hydrological sensor."""
|
"""Setup the Swiss hydrological sensor."""
|
||||||
from bs4 import BeautifulSoup
|
import xmltodict
|
||||||
|
|
||||||
station = config.get(CONF_STATION)
|
station = config.get(CONF_STATION)
|
||||||
name = config.get(CONF_NAME, DEFAULT_NAME)
|
name = config.get(CONF_NAME, DEFAULT_NAME)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(_RESOURCE, timeout=5)
|
response = requests.get(_RESOURCE, timeout=5)
|
||||||
if BeautifulSoup(
|
if any(str(station) == location.get('@StrNr') for location in
|
||||||
response.content,
|
xmltodict.parse(response.text)['AKT_Data']['MesPar']) is False:
|
||||||
'html.parser').find(strnr='{}'.format(station)) is None:
|
_LOGGER.error('The given station does not exist: %s', station)
|
||||||
_LOGGER.error('The given station does not seem to exist: %s',
|
|
||||||
station)
|
|
||||||
return False
|
return False
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
_LOGGER.error('The URL is not accessible')
|
_LOGGER.error('The URL is not accessible')
|
||||||
|
@ -94,29 +86,47 @@ class SwissHydrologicalDataSensor(Entity):
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self):
|
def unit_of_measurement(self):
|
||||||
"""Return the unit of measurement of this entity, if any."""
|
"""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
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""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
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes."""
|
"""Return the state attributes."""
|
||||||
|
attributes = {}
|
||||||
if self.data.measurings is not None:
|
if self.data.measurings is not None:
|
||||||
return {
|
if '02' in self.data.measurings:
|
||||||
ATTR_LOCATION: self.data.measurings.location,
|
attributes[ATTR_WATERLEVEL] = self.data.measurings['02'][
|
||||||
ATTR_UPDATE: self.data.measurings.update_time,
|
'current']
|
||||||
ATTR_DISCHARGE: self.data.measurings.discharge,
|
attributes[ATTR_WATERLEVEL_MEAN] = self.data.measurings['02'][
|
||||||
ATTR_WATERLEVEL: self.data.measurings.waterlevel,
|
'mean']
|
||||||
ATTR_DISCHARGE_MEAN: self.data.measurings.discharge_mean,
|
attributes[ATTR_WATERLEVEL_MAX] = self.data.measurings['02'][
|
||||||
ATTR_WATERLEVEL_MEAN: self.data.measurings.waterlevel_mean,
|
'max']
|
||||||
ATTR_TEMPERATURE_MEAN: self.data.measurings.temperature_mean,
|
if '03' in self.data.measurings:
|
||||||
ATTR_DISCHARGE_MAX: self.data.measurings.discharge_max,
|
attributes[ATTR_TEMPERATURE_MEAN] = self.data.measurings['03'][
|
||||||
ATTR_WATERLEVEL_MAX: self.data.measurings.waterlevel_max,
|
'mean']
|
||||||
ATTR_TEMPERATURE_MAX: self.data.measurings.temperature_max,
|
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
|
@property
|
||||||
def icon(self):
|
def icon(self):
|
||||||
|
@ -128,7 +138,10 @@ class SwissHydrologicalDataSensor(Entity):
|
||||||
"""Get the latest data and update the states."""
|
"""Get the latest data and update the states."""
|
||||||
self.data.update()
|
self.data.update()
|
||||||
if self.data.measurings is not None:
|
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
|
# pylint: disable=too-few-public-methods
|
||||||
|
@ -143,28 +156,33 @@ class HydrologicalData(object):
|
||||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest data from hydrodata.ch."""
|
"""Get the latest data from hydrodata.ch."""
|
||||||
from bs4 import BeautifulSoup
|
import xmltodict
|
||||||
|
|
||||||
|
details = {}
|
||||||
try:
|
try:
|
||||||
response = requests.get(_RESOURCE, timeout=5)
|
response = requests.get(_RESOURCE, timeout=5)
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
_LOGGER.error('Unable to retrieve data from %s', _RESOURCE)
|
_LOGGER.error('Unable to retrieve data from %s', _RESOURCE)
|
||||||
|
|
||||||
try:
|
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"
|
# Water level: Typ="02", temperature: Typ="03", discharge: Typ="10"
|
||||||
type02, type03, type10 = [
|
for station in stations:
|
||||||
soup.find(strnr='{}'.format(self.station), typ='{}'.format(i))
|
if str(self.station) != station.get('@StrNr'):
|
||||||
for i in ['02', '03', '10']]
|
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 = []
|
details['location'] = station.get('Name')
|
||||||
for entry in [type02, type03, type10]:
|
details['update_time'] = station.get('Zeit')
|
||||||
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)
|
|
||||||
|
|
||||||
self.measurings = HydroData._make(details)
|
self.measurings = details
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.measurings = None
|
self.measurings = None
|
||||||
|
|
|
@ -18,8 +18,7 @@ from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
REQUIREMENTS = ['xmltodict==0.10.2']
|
||||||
REQUIREMENTS = ['xmltodict']
|
|
||||||
|
|
||||||
# Sensor types are defined like so:
|
# Sensor types are defined like so:
|
||||||
SENSOR_TYPES = {
|
SENSOR_TYPES = {
|
||||||
|
|
|
@ -30,9 +30,6 @@ apcaccess==0.0.4
|
||||||
# homeassistant.components.sun
|
# homeassistant.components.sun
|
||||||
astral==1.2
|
astral==1.2
|
||||||
|
|
||||||
# homeassistant.components.sensor.swiss_hydrological_data
|
|
||||||
beautifulsoup4==4.4.1
|
|
||||||
|
|
||||||
# homeassistant.components.light.blinksticklight
|
# homeassistant.components.light.blinksticklight
|
||||||
blinkstick==1.1.7
|
blinkstick==1.1.7
|
||||||
|
|
||||||
|
@ -440,8 +437,9 @@ websocket-client==0.37.0
|
||||||
# homeassistant.components.zigbee
|
# homeassistant.components.zigbee
|
||||||
xbee-helper==0.0.7
|
xbee-helper==0.0.7
|
||||||
|
|
||||||
|
# homeassistant.components.sensor.swiss_hydrological_data
|
||||||
# homeassistant.components.sensor.yr
|
# homeassistant.components.sensor.yr
|
||||||
xmltodict
|
xmltodict==0.10.2
|
||||||
|
|
||||||
# homeassistant.components.sensor.yweather
|
# homeassistant.components.sensor.yweather
|
||||||
yahooweather==0.4
|
yahooweather==0.4
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue