diff --git a/homeassistant/components/binary_sensor/wirelesstag.py b/homeassistant/components/binary_sensor/wirelesstag.py index 4bec3a824c395a72b878e7abc3886a0b77312e0f..190b408abf38dfe8740aff62e7d7423eff8ddc37 100644 --- a/homeassistant/components/binary_sensor/wirelesstag.py +++ b/homeassistant/components/binary_sensor/wirelesstag.py @@ -14,9 +14,6 @@ from homeassistant.components.binary_sensor import ( 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 ( @@ -30,7 +27,7 @@ _LOGGER = logging.getLogger(__name__) # 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 @@ -55,49 +52,21 @@ SENSOR_LIGHT = 'light' 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' } @@ -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, @@ -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 @@ -190,7 +121,12 @@ class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorDevice): @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): @@ -198,9 +134,7 @@ class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorDevice): 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.""" @@ -208,7 +142,7 @@ class WirelessTagBinarySensor(WirelessTagBaseSensor, BinarySensorDevice): @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() diff --git a/homeassistant/components/sensor/wirelesstag.py b/homeassistant/components/sensor/wirelesstag.py index a68fb5d0caff39c8aa3f3ddee7cb7c45917bf631..eb9ce2970653b7bc933ee6ab3b4aeca5eb95a0c3 100644 --- a/homeassistant/components/sensor/wirelesstag.py +++ b/homeassistant/components/sensor/wirelesstag.py @@ -15,13 +15,9 @@ from homeassistant.const import ( 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'] @@ -32,24 +28,12 @@ SENSOR_HUMIDITY = 'humidity' 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=[]): @@ -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)) @@ -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: @@ -118,7 +77,7 @@ class WirelessTagSensor(WirelessTagBaseSensor): """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 @@ -144,33 +103,23 @@ class WirelessTagSensor(WirelessTagBaseSensor): @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() diff --git a/homeassistant/components/switch/wirelesstag.py b/homeassistant/components/switch/wirelesstag.py index 5796216d50f30a88e29f1df8de9732fca053db5e..cbe62d107da5439d1d1564e0e56af2a5210a77a7 100644 --- a/homeassistant/components/switch/wirelesstag.py +++ b/homeassistant/components/switch/wirelesstag.py @@ -11,9 +11,6 @@ import voluptuous as vol 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 ( @@ -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) @@ -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) diff --git a/homeassistant/components/wirelesstag.py b/homeassistant/components/wirelesstag.py index 19fb2d40b5d8e8abfd8527b99ac7165c0fdc8e69..f2832100066152546dfa25e553f582cb130ea570 100644 --- a/homeassistant/components/wirelesstag.py +++ b/homeassistant/components/wirelesstag.py @@ -4,6 +4,7 @@ Wireless Sensor Tags platform support. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/wirelesstag/ """ + import logging from requests.exceptions import HTTPError, ConnectTimeout @@ -11,17 +12,18 @@ import voluptuous as vol from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, CONF_USERNAME, CONF_PASSWORD) import homeassistant.helpers.config_validation as cv +from homeassistant import util from homeassistant.helpers.entity import Entity from homeassistant.helpers.dispatcher import ( dispatcher_send) -REQUIREMENTS = ['wirelesstagpy==0.3.0'] +REQUIREMENTS = ['wirelesstagpy==0.4.0'] _LOGGER = logging.getLogger(__name__) -# straight of signal in dBm -ATTR_TAG_SIGNAL_STRAIGHT = 'signal_straight' +# strength of signal in dBm +ATTR_TAG_SIGNAL_STRENGTH = 'signal_strength' # indicates if tag is out of range or not ATTR_TAG_OUT_OF_RANGE = 'out_of_range' # number in percents from max power of tag receiver @@ -34,13 +36,13 @@ NOTIFICATION_TITLE = "Wireless Sensor Tag Setup" DOMAIN = 'wirelesstag' DEFAULT_ENTITY_NAMESPACE = 'wirelesstag' -WIRELESSTAG_TYPE_13BIT = 13 -WIRELESSTAG_TYPE_ALSPRO = 26 -WIRELESSTAG_TYPE_WATER = 32 -WIRELESSTAG_TYPE_WEMO_DEVICE = 82 +# template for signal - first parameter is tag_id, +# second, tag manager mac address +SIGNAL_TAG_UPDATE = 'wirelesstag.tag_info_updated_{}_{}' -SIGNAL_TAG_UPDATE = 'wirelesstag.tag_info_updated_{}' -SIGNAL_BINARY_EVENT_UPDATE = 'wirelesstag.binary_event_updated_{}_{}' +# template for signal - tag_id, sensor type and +# tag manager mac address +SIGNAL_BINARY_EVENT_UPDATE = 'wirelesstag.binary_event_updated_{}_{}_{}' CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -58,6 +60,12 @@ class WirelessTagPlatform: self.hass = hass self.api = api self.tags = {} + self._local_base_url = None + + @property + def tag_manager_macs(self): + """Return list of tag managers mac addresses in user account.""" + return self.api.mac_addresses def load_tags(self): """Load tags from remote server.""" @@ -69,72 +77,84 @@ class WirelessTagPlatform: func_name = 'arm_{}'.format(switch.sensor_type) arm_func = getattr(self.api, func_name) if arm_func is not None: - arm_func(switch.tag_id) + arm_func(switch.tag_id, switch.tag_manager_mac) def disarm(self, switch): """Disarm entity sensor monitoring.""" func_name = 'disarm_{}'.format(switch.sensor_type) disarm_func = getattr(self.api, func_name) if disarm_func is not None: - disarm_func(switch.tag_id) + disarm_func(switch.tag_id, switch.tag_manager_mac) - # pylint: disable=no-self-use - def make_push_notitication(self, name, url, content): - """Create notification config.""" - from wirelesstagpy import NotificationConfig - return NotificationConfig(name, { - 'url': url, 'verb': 'POST', - 'content': content, 'disabled': False, 'nat': True}) - - def install_push_notifications(self, binary_sensors): - """Set up local push notification from tag manager.""" - _LOGGER.info("Registering local push notifications.") + def make_notifications(self, binary_sensors, mac): + """Create configurations for push notifications.""" + _LOGGER.info("Creating configurations for push notifications.") configs = [] - binary_url = self.binary_event_callback_url - for event in binary_sensors: - for state, name in event.binary_spec.items(): - content = ('{"type": "' + event.device_class + - '", "id":{' + str(event.tag_id_index_template) + - '}, "state": \"' + state + '\"}') - config = self.make_push_notitication(name, binary_url, content) - configs.append(config) - - content = ("{\"name\":\"{0}\",\"id\":{1},\"temp\":{2}," + - "\"cap\":{3},\"lux\":{4}}") + bi_url = self.binary_event_callback_url + for bi_sensor in binary_sensors: + configs.extend(bi_sensor.event.build_notifications(bi_url, mac)) + update_url = self.update_callback_url - update_config = self.make_push_notitication( - 'update', update_url, content) + from wirelesstagpy import NotificationConfig as NC + update_config = NC.make_config_for_update_event(update_url, mac) + configs.append(update_config) + return configs - result = self.api.install_push_notification(0, configs, True) - if not result: - self.hass.components.persistent_notification.create( - "Error: failed to install local push notifications<br />", - title="Wireless Sensor Tag Setup Local Push Notifications", - notification_id="wirelesstag_failed_push_notification") - else: - _LOGGER.info("Installed push notifications for all tags.") + def install_push_notifications(self, binary_sensors): + """Register local push notification from tag manager.""" + _LOGGER.info("Registering local push notifications.") + for mac in self.tag_manager_macs: + configs = self.make_notifications(binary_sensors, mac) + # install notifications for all tags in tag manager + # specified by mac + result = self.api.install_push_notification(0, configs, True, mac) + if not result: + self.hass.components.persistent_notification.create( + "Error: failed to install local push notifications <br />", + title="Wireless Sensor Tag Setup Local Push Notifications", + notification_id="wirelesstag_failed_push_notification") + else: + _LOGGER.info("Installed push notifications for all\ + tags in %s.", mac) + + @property + def local_base_url(self): + """Define base url of hass in local network.""" + if self._local_base_url is None: + self._local_base_url = "http://{}".format(util.get_local_ip()) + + port = self.hass.config.api.port + if port is not None: + self._local_base_url += ':{}'.format(port) + return self._local_base_url @property def update_callback_url(self): """Return url for local push notifications(update event).""" return '{}/api/events/wirelesstag_update_tags'.format( - self.hass.config.api.base_url) + self.local_base_url) @property def binary_event_callback_url(self): """Return url for local push notifications(binary event).""" return '{}/api/events/wirelesstag_binary_event'.format( - self.hass.config.api.base_url) + self.local_base_url) def handle_update_tags_event(self, event): """Handle push event from wireless tag manager.""" _LOGGER.info("push notification for update arrived: %s", event) - dispatcher_send( - self.hass, - SIGNAL_TAG_UPDATE.format(event.data.get('id')), - event) + try: + tag_id = event.data.get('id') + mac = event.data.get('mac') + dispatcher_send( + self.hass, + SIGNAL_TAG_UPDATE.format(tag_id, mac), + event) + except Exception as ex: # pylint: disable=broad-except + _LOGGER.error("Unable to handle tag update event:\ + %s error: %s", str(event), str(ex)) def handle_binary_event(self, event): """Handle push notifications for binary (on/off) events.""" @@ -142,12 +162,13 @@ class WirelessTagPlatform: try: tag_id = event.data.get('id') event_type = event.data.get('type') + mac = event.data.get('mac') dispatcher_send( self.hass, - SIGNAL_BINARY_EVENT_UPDATE.format(tag_id, event_type), + SIGNAL_BINARY_EVENT_UPDATE.format(tag_id, event_type, mac), event) except Exception as ex: # pylint: disable=broad-except - _LOGGER.error("Unable to handle binary event:\ + _LOGGER.error("Unable to handle tag binary event:\ %s error: %s", str(event), str(ex)) @@ -193,6 +214,7 @@ class WirelessTagBaseSensor(Entity): self._tag = tag self._uuid = self._tag.uuid self.tag_id = self._tag.tag_id + self.tag_manager_mac = self._tag.tag_manager_mac self._name = self._tag.name self._state = None @@ -251,8 +273,8 @@ class WirelessTagBaseSensor(Entity): return { ATTR_BATTERY_LEVEL: self._tag.battery_remaining, ATTR_VOLTAGE: '{:.2f}V'.format(self._tag.battery_volts), - ATTR_TAG_SIGNAL_STRAIGHT: '{}dBm'.format( - self._tag.signal_straight), + ATTR_TAG_SIGNAL_STRENGTH: '{}dBm'.format( + self._tag.signal_strength), ATTR_TAG_OUT_OF_RANGE: not self._tag.is_in_range, ATTR_TAG_POWER_CONSUMPTION: '{:.2f}%'.format( self._tag.power_consumption) diff --git a/requirements_all.txt b/requirements_all.txt index a5936d14169e5196e4ff0ea72b7eddc253cee027..88ce55d0515596c0e78271b04a70f7235c50f554 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1485,7 +1485,7 @@ websocket-client==0.37.0 websockets==6.0 # homeassistant.components.wirelesstag -wirelesstagpy==0.3.0 +wirelesstagpy==0.4.0 # homeassistant.components.zigbee xbee-helper==0.0.7