mirror of https://github.com/home-assistant/core
Fix BOM weather '-' value (#14042)
This commit is contained in:
parent
ff01aa40c9
commit
e12994a0cd
|
@ -19,8 +19,8 @@ import voluptuous as vol
|
|||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (
|
||||
CONF_MONITORED_CONDITIONS, TEMP_CELSIUS, STATE_UNKNOWN, CONF_NAME,
|
||||
ATTR_ATTRIBUTION, CONF_LATITUDE, CONF_LONGITUDE)
|
||||
CONF_MONITORED_CONDITIONS, TEMP_CELSIUS, CONF_NAME, ATTR_ATTRIBUTION,
|
||||
CONF_LATITUDE, CONF_LONGITUDE)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.util import Throttle
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
@ -145,21 +145,18 @@ class BOMCurrentSensor(Entity):
|
|||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
if self.bom_data.data and self._condition in self.bom_data.data:
|
||||
return self.bom_data.data[self._condition]
|
||||
|
||||
return STATE_UNKNOWN
|
||||
return self.bom_data.get_reading(self._condition)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the device."""
|
||||
attr = {}
|
||||
attr['Sensor Id'] = self._condition
|
||||
attr['Zone Id'] = self.bom_data.data['history_product']
|
||||
attr['Station Id'] = self.bom_data.data['wmo']
|
||||
attr['Station Name'] = self.bom_data.data['name']
|
||||
attr['Zone Id'] = self.bom_data.latest_data['history_product']
|
||||
attr['Station Id'] = self.bom_data.latest_data['wmo']
|
||||
attr['Station Name'] = self.bom_data.latest_data['name']
|
||||
attr['Last Update'] = datetime.datetime.strptime(str(
|
||||
self.bom_data.data['local_date_time_full']), '%Y%m%d%H%M%S')
|
||||
self.bom_data.latest_data['local_date_time_full']), '%Y%m%d%H%M%S')
|
||||
attr[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION
|
||||
return attr
|
||||
|
||||
|
@ -180,22 +177,43 @@ class BOMCurrentData(object):
|
|||
"""Initialize the data object."""
|
||||
self._hass = hass
|
||||
self._zone_id, self._wmo_id = station_id.split('.')
|
||||
self.data = None
|
||||
self._data = None
|
||||
|
||||
def _build_url(self):
|
||||
url = _RESOURCE.format(self._zone_id, self._zone_id, self._wmo_id)
|
||||
_LOGGER.info("BOM URL %s", url)
|
||||
return url
|
||||
|
||||
@property
|
||||
def latest_data(self):
|
||||
"""Return the latest data object."""
|
||||
if self._data:
|
||||
return self._data[0]
|
||||
return None
|
||||
|
||||
def get_reading(self, condition):
|
||||
"""Return the value for the given condition.
|
||||
|
||||
BOM weather publishes condition readings for weather (and a few other
|
||||
conditions) at intervals throughout the day. To avoid a `-` value in
|
||||
the frontend for these conditions, we traverse the historical data
|
||||
for the latest value that is not `-`.
|
||||
|
||||
Iterators are used in this method to avoid iterating needlessly
|
||||
iterating through the entire BOM provided dataset
|
||||
"""
|
||||
condition_readings = (entry[condition] for entry in self._data)
|
||||
return next((x for x in condition_readings if x != '-'), None)
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Get the latest data from BOM."""
|
||||
try:
|
||||
result = requests.get(self._build_url(), timeout=10).json()
|
||||
self.data = result['observations']['data'][0]
|
||||
self._data = result['observations']['data']
|
||||
except ValueError as err:
|
||||
_LOGGER.error("Check BOM %s", err.args)
|
||||
self.data = None
|
||||
self._data = None
|
||||
raise
|
||||
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ class BOMWeather(WeatherEntity):
|
|||
def __init__(self, bom_data, stationname=None):
|
||||
"""Initialise the platform with a data instance and station name."""
|
||||
self.bom_data = bom_data
|
||||
self.stationname = stationname or self.bom_data.data.get('name')
|
||||
self.stationname = stationname or self.bom_data.latest_data.get('name')
|
||||
|
||||
def update(self):
|
||||
"""Update current conditions."""
|
||||
|
@ -62,14 +62,14 @@ class BOMWeather(WeatherEntity):
|
|||
@property
|
||||
def condition(self):
|
||||
"""Return the current condition."""
|
||||
return self.bom_data.data.get('weather')
|
||||
return self.bom_data.get_reading('weather')
|
||||
|
||||
# Now implement the WeatherEntity interface
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""Return the platform temperature."""
|
||||
return self.bom_data.data.get('air_temp')
|
||||
return self.bom_data.get_reading('air_temp')
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
|
@ -79,17 +79,17 @@ class BOMWeather(WeatherEntity):
|
|||
@property
|
||||
def pressure(self):
|
||||
"""Return the mean sea-level pressure."""
|
||||
return self.bom_data.data.get('press_msl')
|
||||
return self.bom_data.get_reading('press_msl')
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
"""Return the relative humidity."""
|
||||
return self.bom_data.data.get('rel_hum')
|
||||
return self.bom_data.get_reading('rel_hum')
|
||||
|
||||
@property
|
||||
def wind_speed(self):
|
||||
"""Return the wind speed."""
|
||||
return self.bom_data.data.get('wind_spd_kmh')
|
||||
return self.bom_data.get_reading('wind_spd_kmh')
|
||||
|
||||
@property
|
||||
def wind_bearing(self):
|
||||
|
@ -99,7 +99,7 @@ class BOMWeather(WeatherEntity):
|
|||
'S', 'SSW', 'SW', 'WSW',
|
||||
'W', 'WNW', 'NW', 'NNW']
|
||||
wind = {name: idx * 360 / 16 for idx, name in enumerate(directions)}
|
||||
return wind.get(self.bom_data.data.get('wind_dir'))
|
||||
return wind.get(self.bom_data.get_reading('wind_dir'))
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
"""The tests for the BOM Weather sensor platform."""
|
||||
import re
|
||||
import unittest
|
||||
import json
|
||||
import requests
|
||||
from unittest.mock import patch
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from homeassistant.setup import setup_component
|
||||
from homeassistant.components import sensor
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component, load_fixture)
|
||||
|
||||
VALID_CONFIG = {
|
||||
'platform': 'bom',
|
||||
'station': 'IDN60901.94767',
|
||||
'name': 'Fake',
|
||||
'monitored_conditions': [
|
||||
'apparent_t',
|
||||
'press',
|
||||
'weather'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def mocked_requests(*args, **kwargs):
|
||||
"""Mock requests.get invocations."""
|
||||
class MockResponse:
|
||||
"""Class to represent a mocked response."""
|
||||
|
||||
def __init__(self, json_data, status_code):
|
||||
"""Initialize the mock response class."""
|
||||
self.json_data = json_data
|
||||
self.status_code = status_code
|
||||
|
||||
def json(self):
|
||||
"""Return the json of the response."""
|
||||
return self.json_data
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
"""Return the content of the response."""
|
||||
return self.json()
|
||||
|
||||
def raise_for_status(self):
|
||||
"""Raise an HTTPError if status is not 200."""
|
||||
if self.status_code != 200:
|
||||
raise requests.HTTPError(self.status_code)
|
||||
|
||||
url = urlparse(args[0])
|
||||
if re.match(r'^/fwo/[\w]+/[\w.]+\.json', url.path):
|
||||
return MockResponse(json.loads(load_fixture('bom_weather.json')), 200)
|
||||
|
||||
raise NotImplementedError('Unknown route {}'.format(url.path))
|
||||
|
||||
|
||||
class TestBOMWeatherSensor(unittest.TestCase):
|
||||
"""Test the BOM Weather sensor."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.config = VALID_CONFIG
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
@patch('requests.get', side_effect=mocked_requests)
|
||||
def test_setup(self, mock_get):
|
||||
"""Test the setup with custom settings."""
|
||||
with assert_setup_component(1, sensor.DOMAIN):
|
||||
self.assertTrue(setup_component(self.hass, sensor.DOMAIN, {
|
||||
'sensor': VALID_CONFIG}))
|
||||
|
||||
fake_entities = [
|
||||
'bom_fake_feels_like_c',
|
||||
'bom_fake_pressure_mb',
|
||||
'bom_fake_weather']
|
||||
|
||||
for entity_id in fake_entities:
|
||||
state = self.hass.states.get('sensor.{}'.format(entity_id))
|
||||
self.assertIsNotNone(state)
|
||||
|
||||
@patch('requests.get', side_effect=mocked_requests)
|
||||
def test_sensor_values(self, mock_get):
|
||||
"""Test retrieval of sensor values."""
|
||||
self.assertTrue(setup_component(
|
||||
self.hass, sensor.DOMAIN, {'sensor': VALID_CONFIG}))
|
||||
|
||||
self.assertEqual('Fine', self.hass.states.get(
|
||||
'sensor.bom_fake_weather').state)
|
||||
self.assertEqual('1021.7', self.hass.states.get(
|
||||
'sensor.bom_fake_pressure_mb').state)
|
||||
self.assertEqual('25.0', self.hass.states.get(
|
||||
'sensor.bom_fake_feels_like_c').state)
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"observations": {
|
||||
"data": [
|
||||
{
|
||||
"wmo": 94767,
|
||||
"name": "Fake",
|
||||
"history_product": "IDN00000",
|
||||
"local_date_time_full": "20180422130000",
|
||||
"apparent_t": 25.0,
|
||||
"press": 1021.7,
|
||||
"weather": "-"
|
||||
},
|
||||
{
|
||||
"wmo": 94767,
|
||||
"name": "Fake",
|
||||
"history_product": "IDN00000",
|
||||
"local_date_time_full": "20180422130000",
|
||||
"apparent_t": 22.0,
|
||||
"press": 1019.7,
|
||||
"weather": "-"
|
||||
},
|
||||
{
|
||||
"wmo": 94767,
|
||||
"name": "Fake",
|
||||
"history_product": "IDN00000",
|
||||
"local_date_time_full": "20180422130000",
|
||||
"apparent_t": 20.0,
|
||||
"press": 1011.7,
|
||||
"weather": "Fine"
|
||||
},
|
||||
{
|
||||
"wmo": 94767,
|
||||
"name": "Fake",
|
||||
"history_product": "IDN00000",
|
||||
"local_date_time_full": "20180422130000",
|
||||
"apparent_t": 18.0,
|
||||
"press": 1010.0,
|
||||
"weather": "-"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue