diff --git a/.coveragerc b/.coveragerc
index e38ceeba5ce..a7d222b33b2 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -311,6 +311,9 @@ omit =
homeassistant/components/wink/*
homeassistant/components/*/wink.py
+ homeassistant/components/wirelesstag.py
+ homeassistant/components/*/wirelesstag.py
+
homeassistant/components/xiaomi_aqara.py
homeassistant/components/*/xiaomi_aqara.py
diff --git a/homeassistant/components/binary_sensor/wirelesstag.py b/homeassistant/components/binary_sensor/wirelesstag.py
new file mode 100644
index 00000000000..bfc2d44fc6e
--- /dev/null
+++ b/homeassistant/components/binary_sensor/wirelesstag.py
@@ -0,0 +1,214 @@
+"""
+Binary sensor support for Wireless Sensor Tags.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/binary_sensor.wirelesstag/
+"""
+import logging
+
+import voluptuous as vol
+
+from homeassistant.core import callback
+from homeassistant.helpers.dispatcher import async_dispatcher_connect
+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 (
+ CONF_MONITORED_CONDITIONS, STATE_ON, STATE_OFF)
+import homeassistant.helpers.config_validation as cv
+
+DEPENDENCIES = ['wirelesstag']
+
+_LOGGER = logging.getLogger(__name__)
+
+# On means in range, Off means out of range
+SENSOR_PRESENCE = 'presence'
+
+# On means motion detected, Off means cear
+SENSOR_MOTION = 'motion'
+
+# On means open, Off means closed
+SENSOR_DOOR = 'door'
+
+# On means temperature become too cold, Off means normal
+SENSOR_COLD = 'cold'
+
+# On means hot, Off means normal
+SENSOR_HEAT = 'heat'
+
+# On means too dry (humidity), Off means normal
+SENSOR_DRY = 'dry'
+
+# On means too wet (humidity), Off means normal
+SENSOR_WET = 'wet'
+
+# On means light detected, Off means no light
+SENSOR_LIGHT = 'light'
+
+# On means moisture detected (wet), Off means no moisture (dry)
+SENSOR_MOISTURE = 'moisture'
+
+# On means tag battery is low, Off means normal
+SENSOR_BATTERY = 'low_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]
+}
+
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+ vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
+ vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
+})
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+ """Set up the platform for a WirelessTags."""
+ platform = hass.data.get(WIRELESSTAG_DOMAIN)
+
+ sensors = []
+ tags = platform.tags
+ for tag in tags.values():
+ allowed_sensor_types = WirelessTagBinarySensor.allowed_sensors(tag)
+ for sensor_type in config.get(CONF_MONITORED_CONDITIONS):
+ if sensor_type in allowed_sensor_types:
+ sensors.append(WirelessTagBinarySensor(platform, tag,
+ sensor_type))
+
+ add_devices(sensors, True)
+ hass.add_job(platform.install_push_notifications, sensors)
+
+
+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]
+
+ async def async_added_to_hass(self):
+ """Register callbacks."""
+ tag_id = self.tag_id
+ event_type = self.device_class
+ async_dispatcher_connect(
+ self.hass,
+ SIGNAL_BINARY_EVENT_UPDATE.format(tag_id, event_type),
+ self._on_binary_event_callback)
+
+ @property
+ def is_on(self):
+ """Return True if the binary sensor is on."""
+ return self._state == STATE_ON
+
+ @property
+ def device_class(self):
+ """Return the class of the binary sensor."""
+ return self._device_class
+
+ @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)
+
+ 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."""
+ # 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
new file mode 100755
index 00000000000..c93da3c791f
--- /dev/null
+++ b/homeassistant/components/sensor/wirelesstag.py
@@ -0,0 +1,176 @@
+"""
+Sensor support for Wirelss Sensor Tags platform.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/sensor.wirelesstag/
+"""
+
+import logging
+import voluptuous as vol
+
+from homeassistant.components.sensor import PLATFORM_SCHEMA
+from homeassistant.core import callback
+from homeassistant.helpers.dispatcher import async_dispatcher_connect
+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']
+
+_LOGGER = logging.getLogger(__name__)
+
+SENSOR_TEMPERATURE = 'temperature'
+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'
+ }
+}
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+ vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
+ vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
+})
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+ """Setup the sensor platform."""
+ platform = hass.data.get(WIRELESSTAG_DOMAIN)
+ sensors = []
+ 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):
+ sensors.append(WirelessTagSensor(
+ platform, tag, sensor_type, hass.config))
+
+ add_devices(sensors, True)
+
+
+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):
+ """Constructor with platform(api), tag and hass sensor type."""
+ 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:
+ # sensor.wirelesstag_bedroom_temperature
+ # and not as sensor.bedroom for temperature and
+ # sensor.bedroom_2 for humidity
+ self._entity_id = '{}.{}_{}_{}'.format('sensor', WIRELESSTAG_DOMAIN,
+ self.underscored_name,
+ self._sensor_type)
+
+ async def async_added_to_hass(self):
+ """Register callbacks."""
+ async_dispatcher_connect(
+ self.hass,
+ SIGNAL_TAG_UPDATE.format(self.tag_id),
+ self._update_tag_info_callback)
+
+ @property
+ def entity_id(self):
+ """Overriden version."""
+ return self._entity_id
+
+ @property
+ def underscored_name(self):
+ """Provide name savvy to be used in entity_id name of self."""
+ return self.name.lower().replace(" ", "_")
+
+ @property
+ def state(self):
+ """Return the state of the sensor."""
+ return self._state
+
+ @property
+ def device_class(self):
+ """Return the class of the sensor."""
+ return self._sensor_type
+
+ @property
+ def unit_of_measurement(self):
+ """Return the unit of measurement."""
+ return self._unit_of_measurement
+
+ @property
+ def principal_value(self):
+ """Return sensor current value."""
+ return getattr(self._tag, self._tag_attr, False)
+
+ @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=W0703
+ _LOGGER.info("Unable to update value of entity: \
+ %s error: %s event: %s", self, error, event)
+
+ 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
new file mode 100644
index 00000000000..cce8c349a31
--- /dev/null
+++ b/homeassistant/components/switch/wirelesstag.py
@@ -0,0 +1,118 @@
+"""
+Switch implementation for Wireless Sensor Tags (wirelesstag.net) platform.
+
+For more details about this platform, please refer to the documentation at
+https://home-assistant.io/components/switch.wirelesstag/
+"""
+import logging
+
+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 (
+ CONF_MONITORED_CONDITIONS)
+import homeassistant.helpers.config_validation as cv
+
+DEPENDENCIES = ['wirelesstag']
+
+_LOGGER = logging.getLogger(__name__)
+
+ARM_TEMPERATURE = 'temperature'
+ARM_HUMIDITY = 'humidity'
+ARM_MOTION = 'motion'
+ARM_LIGHT = 'light'
+ARM_MOISTURE = 'moisture'
+
+# Switch types: Name, tag sensor type
+SWITCH_TYPES = {
+ ARM_TEMPERATURE: ['Arm Temperature', 'temperature'],
+ ARM_HUMIDITY: ['Arm Humidity', 'humidity'],
+ ARM_MOTION: ['Arm Motion', 'motion'],
+ ARM_LIGHT: ['Arm Light', 'light'],
+ ARM_MOISTURE: ['Arm Moisture', 'moisture']
+}
+
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
+ vol.Required(CONF_MONITORED_CONDITIONS, default=[]):
+ vol.All(cv.ensure_list, [vol.In(SWITCH_TYPES)]),
+})
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+ """Set up switches for a Wireless Sensor Tags."""
+ platform = hass.data.get(WIRELESSTAG_DOMAIN)
+
+ switches = []
+ 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):
+ switches.append(WirelessTagSwitch(platform, tag, switch_type))
+
+ add_devices(switches, True)
+
+
+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)
+ self._switch_type = switch_type
+ self.sensor_type = SWITCH_TYPES[self._switch_type][1]
+ self._name = '{} {}'.format(self._tag.name,
+ SWITCH_TYPES[self._switch_type][0])
+
+ def turn_on(self, **kwargs):
+ """Turn on the switch."""
+ self._api.arm(self)
+
+ def turn_off(self, **kwargs):
+ """Turn on the switch."""
+ self._api.disarm(self)
+
+ @property
+ def is_on(self) -> bool:
+ """Return True if entity is on."""
+ return self._state
+
+ def updated_state_value(self):
+ """Provide formatted value."""
+ return self.principal_value
+
+ @property
+ def principal_value(self):
+ """Provide actual value of switch."""
+ attr_name = 'is_{}_sensor_armed'.format(self.sensor_type)
+ return getattr(self._tag, attr_name, False)
diff --git a/homeassistant/components/wirelesstag.py b/homeassistant/components/wirelesstag.py
new file mode 100644
index 00000000000..9fabcb1cd5a
--- /dev/null
+++ b/homeassistant/components/wirelesstag.py
@@ -0,0 +1,256 @@
+"""
+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
+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.helpers.entity import Entity
+from homeassistant.helpers.dispatcher import (
+ dispatcher_send)
+
+REQUIREMENTS = ['wirelesstagpy==0.3.0']
+
+_LOGGER = logging.getLogger(__name__)
+
+
+# straight of signal in dBm
+ATTR_TAG_SIGNAL_STRAIGHT = 'signal_straight'
+# 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
+ATTR_TAG_POWER_CONSUMPTION = 'power_consumption'
+
+
+NOTIFICATION_ID = 'wirelesstag_notification'
+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
+
+SIGNAL_TAG_UPDATE = 'wirelesstag.tag_info_updated_{}'
+SIGNAL_BINARY_EVENT_UPDATE = 'wirelesstag.binary_event_updated_{}_{}'
+
+CONFIG_SCHEMA = vol.Schema({
+ DOMAIN: vol.Schema({
+ vol.Required(CONF_USERNAME): cv.string,
+ vol.Required(CONF_PASSWORD): cv.string,
+ }),
+}, extra=vol.ALLOW_EXTRA)
+
+
+class WirelessTagPlatform:
+ """Principal object to manage all registered in HA tags."""
+
+ def __init__(self, hass, api):
+ """Designated initializer for wirelesstags platform."""
+ self.hass = hass
+ self.api = api
+ self.tags = {}
+
+ def load_tags(self):
+ """Load tags from remote server."""
+ self.tags = self.api.load_tags()
+ return self.tags
+
+ def arm(self, switch):
+ """Arm entity sensor monitoring."""
+ 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)
+
+ 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)
+
+ # pylint: disable=no-self-use
+ def make_push_notitication(self, name, url, content):
+ """Factory for 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):
+ """Setup local push notification from tag manager."""
+ _LOGGER.info("Registering local 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}}")
+ update_url = self.update_callback_url
+ update_config = self.make_push_notitication(
+ 'update', update_url, content)
+ configs.append(update_config)
+
+ 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
",
+ title="Wireless Sensor Tag Setup Local Push Notifications",
+ notification_id="wirelesstag_failed_push_notification")
+ else:
+ _LOGGER.info("Installed push notifications for all tags.")
+
+ @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)
+
+ @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)
+
+ def handle_update_tags_event(self, event):
+ """Main entry to 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)
+
+ def handle_binary_event(self, event):
+ """Handle push notifications for binary (on/off) events."""
+ _LOGGER.info("Push notification for binary event arrived: %s", event)
+ try:
+ tag_id = event.data.get('id')
+ event_type = event.data.get('type')
+ dispatcher_send(
+ self.hass,
+ SIGNAL_BINARY_EVENT_UPDATE.format(tag_id, event_type),
+ event)
+ except Exception as ex: # pylint: disable=W0703
+ _LOGGER.error("Unable to handle binary event:\
+ %s error: %s", str(event), str(ex))
+
+
+def setup(hass, config):
+ """Set up the Wireless Sensor Tag component."""
+ conf = config[DOMAIN]
+ username = conf.get(CONF_USERNAME)
+ password = conf.get(CONF_PASSWORD)
+
+ try:
+ from wirelesstagpy import (WirelessTags, WirelessTagsException)
+ wirelesstags = WirelessTags(username=username, password=password)
+
+ platform = WirelessTagPlatform(hass, wirelesstags)
+ platform.load_tags()
+ hass.data[DOMAIN] = platform
+ except (ConnectTimeout, HTTPError, WirelessTagsException) as ex:
+ _LOGGER.error("Unable to connect to wirelesstag.net service: %s",
+ str(ex))
+ hass.components.persistent_notification.create(
+ "Error: {}
"
+ "Please restart hass after fixing this."
+ "".format(ex),
+ title=NOTIFICATION_TITLE,
+ notification_id=NOTIFICATION_ID)
+ return False
+
+ # listen to custom events
+ hass.bus.listen('wirelesstag_update_tags',
+ hass.data[DOMAIN].handle_update_tags_event)
+ hass.bus.listen('wirelesstag_binary_event',
+ hass.data[DOMAIN].handle_binary_event)
+
+ return True
+
+
+class WirelessTagBaseSensor(Entity):
+ """Base class for HA implementation for Wireless Sensor Tag."""
+
+ def __init__(self, api, tag):
+ """Initialize a base sensor for Wireless Sensor Tag platform."""
+ self._api = api
+ self._tag = tag
+ self._uuid = self._tag.uuid
+ self.tag_id = self._tag.tag_id
+ self._name = self._tag.name
+ self._state = None
+
+ @property
+ def should_poll(self):
+ """Return the polling state."""
+ return True
+
+ @property
+ def name(self):
+ """Return the name of the sensor."""
+ return self._name
+
+ @property
+ def principal_value(self):
+ """Return base value.
+
+ Subclasses need override based on type of sensor.
+ """
+ return 0
+
+ def updated_state_value(self):
+ """Default implementation formats princial value."""
+ return self.decorate_value(self.principal_value)
+
+ # pylint: disable=no-self-use
+ def decorate_value(self, value):
+ """Decorate input value to be well presented for end user."""
+ return '{:.1f}'.format(value)
+
+ @property
+ def available(self):
+ """Return True if entity is available."""
+ return self._tag.is_alive
+
+ def update(self):
+ """Update state."""
+ if not self.should_poll:
+ return
+
+ updated_tags = self._api.load_tags()
+ updated_tag = updated_tags[self._uuid]
+ if updated_tag is None:
+ _LOGGER.error('Unable to update tag: "%s"', self.name)
+ return
+
+ self._tag = updated_tag
+ self._state = self.updated_state_value()
+
+ @property
+ def device_state_attributes(self):
+ """Return the state attributes."""
+ 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_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 d2b52d17961..6bbe2a0b79e 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1378,6 +1378,9 @@ websocket-client==0.37.0
# homeassistant.components.media_player.webostv
websockets==3.2
+# homeassistant.components.wirelesstag
+wirelesstagpy==0.3.0
+
# homeassistant.components.zigbee
xbee-helper==0.0.7