Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple tag managers for Wireless Sensor Tags. #16353

Merged
merged 4 commits into from Sep 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
114 changes: 24 additions & 90 deletions homeassistant/components/binary_sensor/wirelesstag.py
Expand Up @@ -14,9 +14,6 @@
BinarySensorDevice, PLATFORM_SCHEMA)
from homeassistant.components.wirelesstag import (
DOMAIN as WIRELESSTAG_DOMAIN,
WIRELESSTAG_TYPE_13BIT, WIRELESSTAG_TYPE_WATER,
WIRELESSTAG_TYPE_ALSPRO,
WIRELESSTAG_TYPE_WEMO_DEVICE,
SIGNAL_BINARY_EVENT_UPDATE,
WirelessTagBaseSensor)
from homeassistant.const import (
Expand All @@ -30,7 +27,7 @@
# On means in range, Off means out of range
SENSOR_PRESENCE = 'presence'

# On means motion detected, Off means cear
# On means motion detected, Off means clear
SENSOR_MOTION = 'motion'

# On means open, Off means closed
Expand All @@ -55,49 +52,21 @@
SENSOR_MOISTURE = 'moisture'

# On means tag battery is low, Off means normal
SENSOR_BATTERY = 'low_battery'
SENSOR_BATTERY = 'battery'

# Sensor types: Name, device_class, push notification type representing 'on',
# attr to check
SENSOR_TYPES = {
SENSOR_PRESENCE: ['Presence', 'presence', 'is_in_range', {
"on": "oor",
"off": "back_in_range"
}, 2],
SENSOR_MOTION: ['Motion', 'motion', 'is_moved', {
"on": "motion_detected",
}, 5],
SENSOR_DOOR: ['Door', 'door', 'is_door_open', {
"on": "door_opened",
"off": "door_closed"
}, 5],
SENSOR_COLD: ['Cold', 'cold', 'is_cold', {
"on": "temp_toolow",
"off": "temp_normal"
}, 4],
SENSOR_HEAT: ['Heat', 'heat', 'is_heat', {
"on": "temp_toohigh",
"off": "temp_normal"
}, 4],
SENSOR_DRY: ['Too dry', 'dry', 'is_too_dry', {
"on": "too_dry",
"off": "cap_normal"
}, 2],
SENSOR_WET: ['Too wet', 'wet', 'is_too_humid', {
"on": "too_humid",
"off": "cap_normal"
}, 2],
SENSOR_LIGHT: ['Light', 'light', 'is_light_on', {
"on": "too_bright",
"off": "light_normal"
}, 1],
SENSOR_MOISTURE: ['Leak', 'moisture', 'is_leaking', {
"on": "water_detected",
"off": "water_dried",
}, 1],
SENSOR_BATTERY: ['Low Battery', 'battery', 'is_battery_low', {
"on": "low_battery"
}, 3]
SENSOR_PRESENCE: 'Presence',
SENSOR_MOTION: 'Motion',
SENSOR_DOOR: 'Door',
SENSOR_COLD: 'Cold',
SENSOR_HEAT: 'Heat',
SENSOR_DRY: 'Too dry',
SENSOR_WET: 'Too wet',
SENSOR_LIGHT: 'Light',
SENSOR_MOISTURE: 'Leak',
SENSOR_BATTERY: 'Low Battery'
}


Expand All @@ -114,7 +83,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
sensors = []
tags = platform.tags
for tag in tags.values():
allowed_sensor_types = WirelessTagBinarySensor.allowed_sensors(tag)
allowed_sensor_types = tag.supported_binary_events_types
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
if sensor_type in allowed_sensor_types:
sensors.append(WirelessTagBinarySensor(platform, tag,
Expand All @@ -127,59 +96,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorDevice):
"""A binary sensor implementation for WirelessTags."""

@classmethod
def allowed_sensors(cls, tag):
"""Return list of allowed sensor types for specific tag type."""
sensors_map = {
# 13-bit tag - allows everything but not light and moisture
WIRELESSTAG_TYPE_13BIT: [
SENSOR_PRESENCE, SENSOR_BATTERY,
SENSOR_MOTION, SENSOR_DOOR,
SENSOR_COLD, SENSOR_HEAT,
SENSOR_DRY, SENSOR_WET],

# Moister/water sensor - temperature and moisture only
WIRELESSTAG_TYPE_WATER: [
SENSOR_PRESENCE, SENSOR_BATTERY,
SENSOR_COLD, SENSOR_HEAT,
SENSOR_MOISTURE],

# ALS Pro: allows everything, but not moisture
WIRELESSTAG_TYPE_ALSPRO: [
SENSOR_PRESENCE, SENSOR_BATTERY,
SENSOR_MOTION, SENSOR_DOOR,
SENSOR_COLD, SENSOR_HEAT,
SENSOR_DRY, SENSOR_WET,
SENSOR_LIGHT],

# Wemo are power switches.
WIRELESSTAG_TYPE_WEMO_DEVICE: [SENSOR_PRESENCE]
}

# allow everything if tag type is unknown
# (i just dont have full catalog of them :))
tag_type = tag.tag_type
fullset = SENSOR_TYPES.keys()
return sensors_map[tag_type] if tag_type in sensors_map else fullset

def __init__(self, api, tag, sensor_type):
"""Initialize a binary sensor for a Wireless Sensor Tags."""
super().__init__(api, tag)
self._sensor_type = sensor_type
self._name = '{0} {1}'.format(self._tag.name,
SENSOR_TYPES[self._sensor_type][0])
self._device_class = SENSOR_TYPES[self._sensor_type][1]
self._tag_attr = SENSOR_TYPES[self._sensor_type][2]
self.binary_spec = SENSOR_TYPES[self._sensor_type][3]
self.tag_id_index_template = SENSOR_TYPES[self._sensor_type][4]
self.event.human_readable_name)

async def async_added_to_hass(self):
"""Register callbacks."""
tag_id = self.tag_id
event_type = self.device_class
mac = self.tag_manager_mac
async_dispatcher_connect(
self.hass,
SIGNAL_BINARY_EVENT_UPDATE.format(tag_id, event_type),
SIGNAL_BINARY_EVENT_UPDATE.format(tag_id, event_type, mac),
self._on_binary_event_callback)

@property
Expand All @@ -190,25 +121,28 @@ def is_on(self):
@property
def device_class(self):
"""Return the class of the binary sensor."""
return self._device_class
return self._sensor_type

@property
def event(self):
"""Binary event of tag."""
return self._tag.event[self._sensor_type]

@property
def principal_value(self):
"""Return value of tag.

Subclasses need override based on type of sensor.
"""
return (
STATE_ON if getattr(self._tag, self._tag_attr, False)
else STATE_OFF)
return STATE_ON if self.event.is_state_on else STATE_OFF

def updated_state_value(self):
"""Use raw princial value."""
return self.principal_value

@callback
def _on_binary_event_callback(self, event):
"""Update state from arrive push notification."""
"""Update state from arrived push notification."""
# state should be 'on' or 'off'
self._state = event.data.get('state')
self.async_schedule_update_ha_state()
83 changes: 16 additions & 67 deletions homeassistant/components/sensor/wirelesstag.py
Expand Up @@ -15,13 +15,9 @@
CONF_MONITORED_CONDITIONS)
from homeassistant.components.wirelesstag import (
DOMAIN as WIRELESSTAG_DOMAIN,
WIRELESSTAG_TYPE_13BIT, WIRELESSTAG_TYPE_WATER,
WIRELESSTAG_TYPE_ALSPRO,
WIRELESSTAG_TYPE_WEMO_DEVICE,
SIGNAL_TAG_UPDATE,
WirelessTagBaseSensor)
import homeassistant.helpers.config_validation as cv
from homeassistant.const import TEMP_CELSIUS

DEPENDENCIES = ['wirelesstag']

Expand All @@ -32,24 +28,12 @@
SENSOR_MOISTURE = 'moisture'
SENSOR_LIGHT = 'light'

SENSOR_TYPES = {
SENSOR_TEMPERATURE: {
'unit': TEMP_CELSIUS,
'attr': 'temperature'
},
SENSOR_HUMIDITY: {
'unit': '%',
'attr': 'humidity'
},
SENSOR_MOISTURE: {
'unit': '%',
'attr': 'moisture'
},
SENSOR_LIGHT: {
'unit': 'lux',
'attr': 'light'
}
}
SENSOR_TYPES = [
SENSOR_TEMPERATURE,
SENSOR_HUMIDITY,
SENSOR_MOISTURE,
SENSOR_LIGHT
]

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
Expand All @@ -64,7 +48,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
tags = platform.tags
for tag in tags.values():
for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
if sensor_type in WirelessTagSensor.allowed_sensors(tag):
if sensor_type in tag.allowed_sensor_types:
sensors.append(WirelessTagSensor(
platform, tag, sensor_type, hass.config))

Expand All @@ -74,36 +58,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class WirelessTagSensor(WirelessTagBaseSensor):
"""Representation of a Sensor."""

@classmethod
def allowed_sensors(cls, tag):
"""Return array of allowed sensor types for tag."""
all_sensors = SENSOR_TYPES.keys()
sensors_per_tag_type = {
WIRELESSTAG_TYPE_13BIT: [
SENSOR_TEMPERATURE,
SENSOR_HUMIDITY],
WIRELESSTAG_TYPE_WATER: [
SENSOR_TEMPERATURE,
SENSOR_MOISTURE],
WIRELESSTAG_TYPE_ALSPRO: [
SENSOR_TEMPERATURE,
SENSOR_HUMIDITY,
SENSOR_LIGHT],
WIRELESSTAG_TYPE_WEMO_DEVICE: []
}

tag_type = tag.tag_type
return (
sensors_per_tag_type[tag_type] if tag_type in sensors_per_tag_type
else all_sensors)

def __init__(self, api, tag, sensor_type, config):
"""Initialize a WirelessTag sensor."""
super().__init__(api, tag)

self._sensor_type = sensor_type
self._tag_attr = SENSOR_TYPES[self._sensor_type]['attr']
self._unit_of_measurement = SENSOR_TYPES[self._sensor_type]['unit']
self._name = self._tag.name

# I want to see entity_id as:
Expand All @@ -118,7 +77,7 @@ async def async_added_to_hass(self):
"""Register callbacks."""
async_dispatcher_connect(
self.hass,
SIGNAL_TAG_UPDATE.format(self.tag_id),
SIGNAL_TAG_UPDATE.format(self.tag_id, self.tag_manager_mac),
self._update_tag_info_callback)

@property
Expand All @@ -144,33 +103,23 @@ def device_class(self):
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return self._unit_of_measurement
return self._sensor.unit

@property
def principal_value(self):
"""Return sensor current value."""
return getattr(self._tag, self._tag_attr, False)
return self._sensor.value

@property
def _sensor(self):
"""Return tag sensor entity."""
return self._tag.sensor[self._sensor_type]

@callback
def _update_tag_info_callback(self, event):
"""Handle push notification sent by tag manager."""
if event.data.get('id') != self.tag_id:
return

_LOGGER.info("Entity to update state: %s event data: %s",
self, event.data)
new_value = self.principal_value
try:
if self._sensor_type == SENSOR_TEMPERATURE:
new_value = event.data.get('temp')
elif (self._sensor_type == SENSOR_HUMIDITY or
self._sensor_type == SENSOR_MOISTURE):
new_value = event.data.get('cap')
elif self._sensor_type == SENSOR_LIGHT:
new_value = event.data.get('lux')
except Exception as error: # pylint: disable=broad-except
_LOGGER.info("Unable to update value of entity: \
%s error: %s event: %s", self, error, event)

new_value = self._sensor.value_from_update_event(event.data)
self._state = self.decorate_value(new_value)
self.async_schedule_update_ha_state()
29 changes: 1 addition & 28 deletions homeassistant/components/switch/wirelesstag.py
Expand Up @@ -11,9 +11,6 @@

from homeassistant.components.wirelesstag import (
DOMAIN as WIRELESSTAG_DOMAIN,
WIRELESSTAG_TYPE_13BIT, WIRELESSTAG_TYPE_WATER,
WIRELESSTAG_TYPE_ALSPRO,
WIRELESSTAG_TYPE_WEMO_DEVICE,
WirelessTagBaseSensor)
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchDevice
from homeassistant.const import (
Expand Down Expand Up @@ -53,7 +50,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
tags = platform.load_tags()
for switch_type in config.get(CONF_MONITORED_CONDITIONS):
for _, tag in tags.items():
if switch_type in WirelessTagSwitch.allowed_switches(tag):
if switch_type in tag.allowed_monitoring_types:
switches.append(WirelessTagSwitch(platform, tag, switch_type))

add_entities(switches, True)
Expand All @@ -62,30 +59,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
class WirelessTagSwitch(WirelessTagBaseSensor, SwitchDevice):
"""A switch implementation for Wireless Sensor Tags."""

@classmethod
def allowed_switches(cls, tag):
"""Return allowed switch types for wireless tag."""
all_sensors = SWITCH_TYPES.keys()
sensors_per_tag_spec = {
WIRELESSTAG_TYPE_13BIT: [
ARM_TEMPERATURE, ARM_HUMIDITY, ARM_MOTION],
WIRELESSTAG_TYPE_WATER: [
ARM_TEMPERATURE, ARM_MOISTURE],
WIRELESSTAG_TYPE_ALSPRO: [
ARM_TEMPERATURE, ARM_HUMIDITY, ARM_MOTION, ARM_LIGHT],
WIRELESSTAG_TYPE_WEMO_DEVICE: []
}

tag_type = tag.tag_type

result = (
sensors_per_tag_spec[tag_type]
if tag_type in sensors_per_tag_spec else all_sensors)
_LOGGER.info("Allowed switches: %s tag_type: %s",
str(result), tag_type)

return result

def __init__(self, api, tag, switch_type):
"""Initialize a switch for Wireless Sensor Tag."""
super().__init__(api, tag)
Expand Down