Skip to content

Commit

Permalink
Encapsulated hw specific logic inside lib. Fixed typo found by cgtobi.
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeymaysak committed Aug 29, 2018
1 parent 0a579ff commit 155231d
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 16 deletions.
3 changes: 1 addition & 2 deletions test/test_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def tests_full_data_load(self, m):
self.assertIsNotNone(sensor.name)
self.assertIsNotNone(sensor.uuid)
self.assertIsNotNone(sensor.tag_manager_mac)
self.assertTrue(sensor.in_celcius)

self.assertIsNotNone(sensor.temperature)
self.assertTrue(isinstance(sensor.temperature, float))
Expand All @@ -62,7 +61,7 @@ def tests_full_data_load(self, m):
self.assertIsNotNone(sensor.tag_type)
self.assertIsNotNone(sensor.comment)
self.assertIsNotNone(sensor.is_alive)
self.assertIsNotNone(sensor.signal_straight)
self.assertIsNotNone(sensor.signal_strength)
self.assertIsNotNone(sensor.beep_option)
self.assertIsNotNone(sensor.is_in_range)
self.assertIsNotNone(sensor.hw_revision)
Expand Down
10 changes: 9 additions & 1 deletion wirelesstagpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def __init__(self, username, password):
# server time in utc (filetime format)
self._server_time = 0

# array of tag managers mac addresses
self.mac_addresses = []

def load_tags(self):
"""Load all registered tags."""
if self._needs_reload:
Expand All @@ -80,7 +83,8 @@ def load_tags(self):
# save mac - a unique identifier of specific tag manager
mac = tag['mac'] if 'mac' in tag else None
self._tags[uuid] = SensorTag(tag, self, mac)
_LOGGER.info("tags reloaded at: %s", datetime.now())
self._register_mac(mac)
_LOGGER.info("Tags reloaded at: %s", datetime.now())
except Exception as error:
_LOGGER.error("failed to load tags - %s", error)

Expand Down Expand Up @@ -263,6 +267,10 @@ def _needs_reload(self):
elapsed = time.time() - self._last_load_time
return elapsed > self._postback_interval

def _register_mac(self, mac):
if mac not in self.mac_addresses:
self.mac_addresses.append(mac)

def __str__(self):
"""Return string representation of wirelesstags platform."""
temperature_str = 'celsius' if self.use_celsius else 'fahrenheit'
Expand Down
50 changes: 50 additions & 0 deletions wirelesstagpy/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,53 @@

LOAD_EVENT_URL_CONFIG_URL = BASEURL + "/ethClient.asmx/LoadEventURLConfig"
SAVE_EVENT_URL_CONFIG_URL = BASEURL + "/ethClient.asmx/SaveEventURLConfig"

WIRELESSTAG_TYPE_13BIT = 13
WIRELESSTAG_TYPE_ALSPRO = 26
WIRELESSTAG_TYPE_WATER = 32
WIRELESSTAG_TYPE_PIR = 72
WIRELESSTAG_TYPE_WEMO_DEVICE = 82

# events
# On means in range, Off means out of range
EVENT_PRESENCE = 'presence'

# On means motion detected, Off means cear
EVENT_MOTION = 'motion'

# On means open, Off means closed
EVENT_DOOR = 'door'

# On means temperature become too cold, Off means normal
EVENT_COLD = 'cold'

# On means hot, Off means normal
EVENT_HEAT = 'heat'

# On means too dry (humidity), Off means normal
EVENT_DRY = 'dry'

# On means too wet (humidity), Off means normal
EVENT_WET = 'wet'

# On means light detected, Off means no light
EVENT_LIGHT = 'light'

# On means moisture detected (wet), Off means no moisture (dry)
EVENT_MOISTURE = 'moisture'

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

# supported sensor metrics
SENSOR_TEMPERATURE = 'temperature'
SENSOR_HUMIDITY = 'humidity'
SENSOR_MOISTURE = 'moisture'
SENSOR_LIGHT = 'light'

# supported actions to monitoring specific metric
ARM_TEMPERATURE = 'temperature'
ARM_HUMIDITY = 'humidity'
ARM_MOTION = 'motion'
ARM_LIGHT = 'light'
ARM_MOISTURE = 'moisture'
7 changes: 7 additions & 0 deletions wirelesstagpy/notificationconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
class NotificationConfig:
"""Model representing push notification configuration for single tag."""

@classmethod
def make_post_local(cls, name, url, content):
"""Create local push notification using POST http verb."""
return cls(name, {'url': url, 'verb': 'POST',
'content': content,
'disabled': False, 'nat': True})

def __init__(self, name, spec):
"""Init with name of event and dictionary."""
self.name = name
Expand Down
144 changes: 131 additions & 13 deletions wirelesstagpy/sensortag.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@
Creation Date: 3/7/2018
"""

import logging
from datetime import datetime

import wirelesstagpy.utils as UTILS
import wirelesstagpy.constants as CONST
from wirelesstagpy.binaryevent import (
BinaryEvent,
BINARY_EVENT_SPECS)

_LOGGER = logging.getLogger(__name__)


class SensorTag:
Expand All @@ -20,7 +28,6 @@ class SensorTag:
def __init__(self, info, platform, mac=None):
"""Init with dictionary and parent WirelessTagPlatform."""
self._info = info
self._api = platform
self.uuid = self._info['uuid']
self.tag_id = self._info['slaveId']
self.name = self._info['name']
Expand All @@ -34,13 +41,8 @@ def __init__(self, info, platform, mac=None):
# mac address of tag manager this instance belong to
self.tag_manager_mac = mac

@property
def in_celcius(self):
"""Return temperature's units of measure.
True for Celcius, False for Fahrenheit.
"""
return self._api.use_celsius
# events_specs
self._event_specs = {}

@property
def battery_remaining(self):
Expand Down Expand Up @@ -73,8 +75,8 @@ def is_alive(self):
return self._info['alive']

@property
def signal_straight(self):
"""Int with signal straight."""
def signal_strength(self):
"""Int with signal strength."""
return self._info['signaldBm']

@property
Expand Down Expand Up @@ -216,20 +218,136 @@ def is_light_on(self):
@property
def is_leaking(self):
"""Return True if detected water leak - applicable for water sensor only."""
return self.tag_type == 32 and self.moisture > 0
return self.tag_type == CONST.WIRELESSTAG_TYPE_WATER and self.moisture > 0

@property
def is_battery_low(self):
"""Return True if detected low battery level."""
return self.battery_volts <= self.low_battery_threshold

@property
def supported_binary_events_types(self):
"""Return supported by tag binary event types (events with state on or off)."""
events_map = {
# 13-bit tag - allows everything but not light and moisture
CONST.WIRELESSTAG_TYPE_13BIT: [
CONST.EVENT_PRESENCE, CONST.EVENT_BATTERY,
CONST.EVENT_MOTION, CONST.EVENT_DOOR,
CONST.EVENT_COLD, CONST.EVENT_HEAT,
CONST.EVENT_DRY, CONST.EVENT_WET],

# Moister/water sensor - temperature and moisture only
CONST.WIRELESSTAG_TYPE_WATER: [
CONST.EVENT_PRESENCE, CONST.EVENT_BATTERY,
CONST.EVENT_COLD, CONST.EVENT_HEAT,
CONST.EVENT_MOISTURE],

# ALS Pro: allows everything, but not moisture
CONST.WIRELESSTAG_TYPE_ALSPRO: [
CONST.EVENT_PRESENCE, CONST.EVENT_BATTERY,
CONST.EVENT_MOTION, CONST.EVENT_DOOR,
CONST.EVENT_COLD, CONST.EVENT_HEAT,
CONST.EVENT_DRY, CONST.EVENT_WET,
CONST.EVENT_LIGHT],

# PIR KumoSensor: specialized on motion, but has
# temperature and humidity as 13-bit tag
CONST.WIRELESSTAG_TYPE_PIR: [
CONST.EVENT_PRESENCE, CONST.EVENT_BATTERY,
CONST.EVENT_MOTION, CONST.EVENT_DOOR,
CONST.EVENT_COLD, CONST.EVENT_HEAT,
CONST.EVENT_DRY, CONST.EVENT_WET
],

# Wemo are power switches.
CONST.WIRELESSTAG_TYPE_WEMO_DEVICE: [CONST.EVENT_PRESENCE]
}

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

def event_for_type(self, event_type):
"""Return event model for specified type."""
if event_type not in self.supported_binary_events_types:
return None

if event_type in self._event_specs:
return self._event_specs[event_type]
else:
event = BinaryEvent.make_event(event_type, self)
self._event_specs[event_type] = event
return event

@property
def allowed_sensor_types(self):
"""Return array of allowed sensor types for tag."""
all_sensors = [CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_HUMIDITY,
CONST.SENSOR_LIGHT]
sensors_per_tag_type = {
CONST.WIRELESSTAG_TYPE_13BIT: [
CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_HUMIDITY],
CONST.WIRELESSTAG_TYPE_WATER: [
CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_MOISTURE],
CONST.WIRELESSTAG_TYPE_ALSPRO: [
CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_HUMIDITY,
CONST.SENSOR_LIGHT],
CONST.WIRELESSTAG_TYPE_PIR: [
CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_HUMIDITY
],
CONST.WIRELESSTAG_TYPE_WEMO_DEVICE: []
}

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

@property
def allowed_monitoring_types(self):
"""Return allowed actions to monitor for tag."""
all_sensors = [
CONST.ARM_TEMPERATURE, CONST.ARM_HUMIDITY,
CONST.ARM_MOTION, CONST.ARM_LIGHT]

sensors_per_tag_spec = {
CONST.WIRELESSTAG_TYPE_13BIT: [
CONST.ARM_TEMPERATURE, CONST.ARM_HUMIDITY, CONST.ARM_MOTION],
CONST.WIRELESSTAG_TYPE_WATER: [
CONST.ARM_TEMPERATURE, CONST.ARM_MOISTURE],
CONST.WIRELESSTAG_TYPE_ALSPRO: [
CONST.ARM_TEMPERATURE, CONST.ARM_HUMIDITY,
CONST.ARM_MOTION, CONST.ARM_LIGHT],
CONST.WIRELESSTAG_TYPE_PIR: [
CONST.ARM_TEMPERATURE, CONST.ARM_HUMIDITY, CONST.ARM_MOTION
],
CONST.WIRELESSTAG_TYPE_WEMO_DEVICE: []
}

tag_type = self.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 __repr__(self):
"""Return string representation of tag."""
# Water/Moisture Sensor supports water and temperature
if self.tag_type == 32:
if self.tag_type == CONST.WIRELESSTAG_TYPE_WATER:
return '{} temp: {} leak: {}'.format(self.name, self.temperature, self.is_leaking)
# ALS Pro (8bit)
elif self.tag_type == 26:
elif self.tag_type == CONST.WIRELESSTAG_TYPE_ALSPRO:
return '{} temp: {} humidity: {} lux: {}'.format(self.name, self.temperature, self.humidity, self.light)

# use 13-bit tag supports temp/motion/humidity as fallback for everything else
Expand Down

0 comments on commit 155231d

Please sign in to comment.