Fix binary sensor in Ambient PWS (#20801)
* Fix binary sensor in Ambient PWS * Correctly load entities * Corrected what on and off means for existing sensor * Make sure to return a boolean * Member comments * Binary sensor doesn't need state property
This commit is contained in:
parent
d45f25ce2c
commit
d24ccbd1e6
4 changed files with 209 additions and 90 deletions
|
@ -12,51 +12,85 @@ from homeassistant.config_entries import SOURCE_IMPORT
|
|||
from homeassistant.const import (
|
||||
ATTR_NAME, ATTR_LOCATION, CONF_API_KEY, CONF_MONITORED_CONDITIONS,
|
||||
EVENT_HOMEASSISTANT_STOP)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect, async_dispatcher_send)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from .config_flow import configured_instances
|
||||
from .const import (
|
||||
ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE)
|
||||
ATTR_LAST_DATA, CONF_APP_KEY, DATA_CLIENT, DOMAIN, TOPIC_UPDATE,
|
||||
TYPE_BINARY_SENSOR, TYPE_SENSOR)
|
||||
|
||||
REQUIREMENTS = ['aioambient==0.1.0']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_SOCKET_MIN_RETRY = 15
|
||||
|
||||
TYPE_24HOURRAININ = '24hourrainin'
|
||||
TYPE_BAROMABSIN = 'baromabsin'
|
||||
TYPE_BAROMRELIN = 'baromrelin'
|
||||
TYPE_BATTOUT = 'battout'
|
||||
TYPE_CO2 = 'co2'
|
||||
TYPE_DAILYRAININ = 'dailyrainin'
|
||||
TYPE_DEWPOINT = 'dewPoint'
|
||||
TYPE_EVENTRAININ = 'eventrainin'
|
||||
TYPE_FEELSLIKE = 'feelsLike'
|
||||
TYPE_HOURLYRAININ = 'hourlyrainin'
|
||||
TYPE_HUMIDITY = 'humidity'
|
||||
TYPE_HUMIDITYIN = 'humidityin'
|
||||
TYPE_LASTRAIN = 'lastRain'
|
||||
TYPE_MAXDAILYGUST = 'maxdailygust'
|
||||
TYPE_MONTHLYRAININ = 'monthlyrainin'
|
||||
TYPE_SOLARRADIATION = 'solarradiation'
|
||||
TYPE_TEMPF = 'tempf'
|
||||
TYPE_TEMPINF = 'tempinf'
|
||||
TYPE_TOTALRAININ = 'totalrainin'
|
||||
TYPE_UV = 'uv'
|
||||
TYPE_WEEKLYRAININ = 'weeklyrainin'
|
||||
TYPE_WINDDIR = 'winddir'
|
||||
TYPE_WINDDIR_AVG10M = 'winddir_avg10m'
|
||||
TYPE_WINDDIR_AVG2M = 'winddir_avg2m'
|
||||
TYPE_WINDGUSTDIR = 'windgustdir'
|
||||
TYPE_WINDGUSTMPH = 'windgustmph'
|
||||
TYPE_WINDSPDMPH_AVG10M = 'windspdmph_avg10m'
|
||||
TYPE_WINDSPDMPH_AVG2M = 'windspdmph_avg2m'
|
||||
TYPE_WINDSPEEDMPH = 'windspeedmph'
|
||||
TYPE_YEARLYRAININ = 'yearlyrainin'
|
||||
SENSOR_TYPES = {
|
||||
'24hourrainin': ('24 Hr Rain', 'in'),
|
||||
'baromabsin': ('Abs Pressure', 'inHg'),
|
||||
'baromrelin': ('Rel Pressure', 'inHg'),
|
||||
'battout': ('Battery', ''),
|
||||
'co2': ('co2', 'ppm'),
|
||||
'dailyrainin': ('Daily Rain', 'in'),
|
||||
'dewPoint': ('Dew Point', '°F'),
|
||||
'eventrainin': ('Event Rain', 'in'),
|
||||
'feelsLike': ('Feels Like', '°F'),
|
||||
'hourlyrainin': ('Hourly Rain Rate', 'in/hr'),
|
||||
'humidity': ('Humidity', '%'),
|
||||
'humidityin': ('Humidity In', '%'),
|
||||
'lastRain': ('Last Rain', ''),
|
||||
'maxdailygust': ('Max Gust', 'mph'),
|
||||
'monthlyrainin': ('Monthly Rain', 'in'),
|
||||
'solarradiation': ('Solar Rad', 'W/m^2'),
|
||||
'tempf': ('Temp', '°F'),
|
||||
'tempinf': ('Inside Temp', '°F'),
|
||||
'totalrainin': ('Lifetime Rain', 'in'),
|
||||
'uv': ('uv', 'Index'),
|
||||
'weeklyrainin': ('Weekly Rain', 'in'),
|
||||
'winddir': ('Wind Dir', '°'),
|
||||
'winddir_avg10m': ('Wind Dir Avg 10m', '°'),
|
||||
'winddir_avg2m': ('Wind Dir Avg 2m', 'mph'),
|
||||
'windgustdir': ('Gust Dir', '°'),
|
||||
'windgustmph': ('Wind Gust', 'mph'),
|
||||
'windspdmph_avg10m': ('Wind Avg 10m', 'mph'),
|
||||
'windspdmph_avg2m': ('Wind Avg 2m', 'mph'),
|
||||
'windspeedmph': ('Wind Speed', 'mph'),
|
||||
'yearlyrainin': ('Yearly Rain', 'in'),
|
||||
TYPE_24HOURRAININ: ('24 Hr Rain', 'in', TYPE_SENSOR, None),
|
||||
TYPE_BAROMABSIN: ('Abs Pressure', 'inHg', TYPE_SENSOR, None),
|
||||
TYPE_BAROMRELIN: ('Rel Pressure', 'inHg', TYPE_SENSOR, None),
|
||||
TYPE_BATTOUT: ('Battery', None, TYPE_BINARY_SENSOR, 'battery'),
|
||||
TYPE_CO2: ('co2', 'ppm', TYPE_SENSOR, None),
|
||||
TYPE_DAILYRAININ: ('Daily Rain', 'in', TYPE_SENSOR, None),
|
||||
TYPE_DEWPOINT: ('Dew Point', '°F', TYPE_SENSOR, None),
|
||||
TYPE_EVENTRAININ: ('Event Rain', 'in', TYPE_SENSOR, None),
|
||||
TYPE_FEELSLIKE: ('Feels Like', '°F', TYPE_SENSOR, None),
|
||||
TYPE_HOURLYRAININ: ('Hourly Rain Rate', 'in/hr', TYPE_SENSOR, None),
|
||||
TYPE_HUMIDITY: ('Humidity', '%', TYPE_SENSOR, None),
|
||||
TYPE_HUMIDITYIN: ('Humidity In', '%', TYPE_SENSOR, None),
|
||||
TYPE_LASTRAIN: ('Last Rain', None, TYPE_SENSOR, None),
|
||||
TYPE_MAXDAILYGUST: ('Max Gust', 'mph', TYPE_SENSOR, None),
|
||||
TYPE_MONTHLYRAININ: ('Monthly Rain', 'in', TYPE_SENSOR, None),
|
||||
TYPE_SOLARRADIATION: ('Solar Rad', 'W/m^2', TYPE_SENSOR, None),
|
||||
TYPE_TEMPF: ('Temp', '°F', TYPE_SENSOR, None),
|
||||
TYPE_TEMPINF: ('Inside Temp', '°F', TYPE_SENSOR, None),
|
||||
TYPE_TOTALRAININ: ('Lifetime Rain', 'in', TYPE_SENSOR, None),
|
||||
TYPE_UV: ('uv', 'Index', TYPE_SENSOR, None),
|
||||
TYPE_WEEKLYRAININ: ('Weekly Rain', 'in', TYPE_SENSOR, None),
|
||||
TYPE_WINDDIR: ('Wind Dir', '°', TYPE_SENSOR, None),
|
||||
TYPE_WINDDIR_AVG10M: ('Wind Dir Avg 10m', '°', TYPE_SENSOR, None),
|
||||
TYPE_WINDDIR_AVG2M: ('Wind Dir Avg 2m', 'mph', TYPE_SENSOR, None),
|
||||
TYPE_WINDGUSTDIR: ('Gust Dir', '°', TYPE_SENSOR, None),
|
||||
TYPE_WINDGUSTMPH: ('Wind Gust', 'mph', TYPE_SENSOR, None),
|
||||
TYPE_WINDSPDMPH_AVG10M: ('Wind Avg 10m', 'mph', TYPE_SENSOR, None),
|
||||
TYPE_WINDSPDMPH_AVG2M: ('Wind Avg 2m', 'mph', TYPE_SENSOR, None),
|
||||
TYPE_WINDSPEEDMPH: ('Wind Speed', 'mph', TYPE_SENSOR, None),
|
||||
TYPE_YEARLYRAININ: ('Yearly Rain', 'in', TYPE_SENSOR, None),
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
|
@ -102,8 +136,7 @@ async def async_setup_entry(hass, config_entry):
|
|||
|
||||
try:
|
||||
ambient = AmbientStation(
|
||||
hass,
|
||||
config_entry,
|
||||
hass, config_entry,
|
||||
Client(
|
||||
config_entry.data[CONF_API_KEY],
|
||||
config_entry.data[CONF_APP_KEY], session),
|
||||
|
@ -126,8 +159,9 @@ async def async_unload_entry(hass, config_entry):
|
|||
ambient = hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
|
||||
hass.async_create_task(ambient.ws_disconnect())
|
||||
|
||||
await hass.config_entries.async_forward_entry_unload(
|
||||
config_entry, 'sensor')
|
||||
for component in ('binary_sensor', 'sensor'):
|
||||
await hass.config_entries.async_forward_entry_unload(
|
||||
config_entry, component)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -178,9 +212,10 @@ class AmbientStation:
|
|||
ATTR_NAME: station['info']['name'],
|
||||
}
|
||||
|
||||
for component in ('binary_sensor', 'sensor'):
|
||||
self._hass.async_create_task(
|
||||
self._hass.config_entries.async_forward_entry_setup(
|
||||
self._config_entry, 'sensor'))
|
||||
self._config_entry, component))
|
||||
|
||||
self._ws_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
||||
|
||||
|
@ -194,8 +229,7 @@ class AmbientStation:
|
|||
except WebsocketError as err:
|
||||
_LOGGER.error("Error with the websocket connection: %s", err)
|
||||
|
||||
self._ws_reconnect_delay = min(
|
||||
2 * self._ws_reconnect_delay, 480)
|
||||
self._ws_reconnect_delay = min(2 * self._ws_reconnect_delay, 480)
|
||||
|
||||
async_call_later(
|
||||
self._hass, self._ws_reconnect_delay, self.ws_connect)
|
||||
|
@ -203,3 +237,49 @@ class AmbientStation:
|
|||
async def ws_disconnect(self):
|
||||
"""Disconnect from the websocket."""
|
||||
await self.client.websocket.disconnect()
|
||||
|
||||
|
||||
class AmbientWeatherEntity(Entity):
|
||||
"""Define a base Ambient PWS entity."""
|
||||
|
||||
def __init__(
|
||||
self, ambient, mac_address, station_name, sensor_type,
|
||||
sensor_name):
|
||||
"""Initialize the sensor."""
|
||||
self._ambient = ambient
|
||||
self._async_unsub_dispatcher_connect = None
|
||||
self._mac_address = mac_address
|
||||
self._sensor_name = sensor_name
|
||||
self._sensor_type = sensor_type
|
||||
self._state = None
|
||||
self._station_name = station_name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return '{0}_{1}'.format(self._station_name, self._sensor_name)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Disable polling."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique, unchanging string that represents this sensor."""
|
||||
return '{0}_{1}'.format(self._mac_address, self._sensor_name)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
@callback
|
||||
def update():
|
||||
"""Update the state."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
|
||||
self.hass, TOPIC_UPDATE, update)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""Disconnect dispatcher listener when removed."""
|
||||
if self._async_unsub_dispatcher_connect:
|
||||
self._async_unsub_dispatcher_connect()
|
||||
|
|
71
homeassistant/components/ambient_station/binary_sensor.py
Normal file
71
homeassistant/components/ambient_station/binary_sensor.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
"""
|
||||
Support for Ambient Weather Station binary sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/binary_sensor.ambient_station/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.ambient_station import (
|
||||
SENSOR_TYPES, TYPE_BATTOUT, AmbientWeatherEntity)
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.const import ATTR_NAME
|
||||
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TYPE_BINARY_SENSOR
|
||||
|
||||
DEPENDENCIES = ['ambient_station']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up Ambient PWS binary sensors based on the old way."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up Ambient PWS binary sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
|
||||
binary_sensor_list = []
|
||||
for mac_address, station in ambient.stations.items():
|
||||
for condition in ambient.monitored_conditions:
|
||||
name, _, kind, device_class = SENSOR_TYPES[condition]
|
||||
if kind == TYPE_BINARY_SENSOR:
|
||||
binary_sensor_list.append(
|
||||
AmbientWeatherBinarySensor(
|
||||
ambient, mac_address, station[ATTR_NAME], condition,
|
||||
name, device_class))
|
||||
|
||||
async_add_entities(binary_sensor_list, True)
|
||||
|
||||
|
||||
class AmbientWeatherBinarySensor(AmbientWeatherEntity, BinarySensorDevice):
|
||||
"""Define an Ambient binary sensor."""
|
||||
|
||||
def __init__(
|
||||
self, ambient, mac_address, station_name, sensor_type, sensor_name,
|
||||
device_class):
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(
|
||||
ambient, mac_address, station_name, sensor_type, sensor_name)
|
||||
|
||||
self._device_class = device_class
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class."""
|
||||
return self._device_class
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return the status of the sensor."""
|
||||
if self._sensor_type == TYPE_BATTOUT:
|
||||
return self._state == 0
|
||||
|
||||
return self._state == 1
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch new state data for the entity."""
|
||||
self._state = self._ambient.stations[
|
||||
self._mac_address][ATTR_LAST_DATA].get(self._sensor_type)
|
|
@ -8,3 +8,6 @@ CONF_APP_KEY = 'app_key'
|
|||
DATA_CLIENT = 'data_client'
|
||||
|
||||
TOPIC_UPDATE = 'update'
|
||||
|
||||
TYPE_BINARY_SENSOR = 'binary_sensor'
|
||||
TYPE_SENSOR = 'sensor'
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
"""
|
||||
Support for Ambient Weather Station Service.
|
||||
Support for Ambient Weather Station sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.ambient_station/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.ambient_station import SENSOR_TYPES
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.ambient_station import (
|
||||
SENSOR_TYPES, AmbientWeatherEntity)
|
||||
from homeassistant.const import ATTR_NAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TOPIC_UPDATE
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN, TYPE_SENSOR
|
||||
|
||||
DEPENDENCIES = ['ambient_station']
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -20,52 +18,39 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
async def async_setup_platform(
|
||||
hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up an Ambient PWS sensor based on existing config."""
|
||||
"""Set up Ambient PWS sensors based on existing config."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up an Ambient PWS sensor based on a config entry."""
|
||||
"""Set up Ambient PWS sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
|
||||
sensor_list = []
|
||||
for mac_address, station in ambient.stations.items():
|
||||
for condition in ambient.monitored_conditions:
|
||||
name, unit = SENSOR_TYPES[condition]
|
||||
sensor_list.append(
|
||||
AmbientWeatherSensor(
|
||||
ambient, mac_address, station[ATTR_NAME], condition, name,
|
||||
unit))
|
||||
name, unit, kind, _ = SENSOR_TYPES[condition]
|
||||
if kind == TYPE_SENSOR:
|
||||
sensor_list.append(
|
||||
AmbientWeatherSensor(
|
||||
ambient, mac_address, station[ATTR_NAME], condition,
|
||||
name, unit))
|
||||
|
||||
async_add_entities(sensor_list, True)
|
||||
|
||||
|
||||
class AmbientWeatherSensor(Entity):
|
||||
class AmbientWeatherSensor(AmbientWeatherEntity):
|
||||
"""Define an Ambient sensor."""
|
||||
|
||||
def __init__(
|
||||
self, ambient, mac_address, station_name, sensor_type, sensor_name,
|
||||
unit):
|
||||
"""Initialize the sensor."""
|
||||
self._ambient = ambient
|
||||
self._async_unsub_dispatcher_connect = None
|
||||
self._mac_address = mac_address
|
||||
self._sensor_name = sensor_name
|
||||
self._sensor_type = sensor_type
|
||||
self._state = None
|
||||
self._station_name = station_name
|
||||
super().__init__(
|
||||
ambient, mac_address, station_name, sensor_type, sensor_name)
|
||||
|
||||
self._unit = unit
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return '{0}_{1}'.format(self._station_name, self._sensor_name)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Disable polling."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
|
@ -76,26 +61,6 @@ class AmbientWeatherSensor(Entity):
|
|||
"""Return the unit of measurement."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique, unchanging string that represents this sensor."""
|
||||
return '{0}_{1}'.format(self._mac_address, self._sensor_name)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
@callback
|
||||
def update():
|
||||
"""Update the state."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
|
||||
self.hass, TOPIC_UPDATE, update)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""Disconnect dispatcher listener when removed."""
|
||||
if self._async_unsub_dispatcher_connect:
|
||||
self._async_unsub_dispatcher_connect()
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch new state data for the sensor."""
|
||||
self._state = self._ambient.stations[
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue