Skip to content

Commit

Permalink
Merge pull request #12 from sergeymaysak/lux_to_lx
Browse files Browse the repository at this point in the history
Outdoor Probe and switch to lx unit of measurement.
  • Loading branch information
sergeymaysak committed Oct 18, 2021
2 parents a005906 + 156d09b commit 4d635c2
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 9 deletions.
54 changes: 54 additions & 0 deletions test/mock/__init__.py
Expand Up @@ -615,3 +615,57 @@
"v2flag": 16,
"batteryRemaining": 0.99
}

OUTDOOR_PROBE = {
"dbid": 2,
"notificationJS": None,
"name": "PoolProbe",
"uuid": "fake-1",
"comment": "",
"slaveId": 11,
"tagType": 42,
"discon": None,
"lastComm": 132655886150468311,
"alive": True,
"signaldBm": -76,
"batteryVolt": 2,
"beeping": False,
"lit": False,
"migrationPending": False,
"beepDurationDefault": 5,
"eventState": 0,
"tempEventState": 2,
"OutOfRange": True,
"tempSpurTh": 28,
"lux": 0,
"temperature": 18.036840438842773,
"tempCalOffset": 0,
"capCalOffset": 0,
"image_md5": None,
"cap": 23.43,
"capRaw": 0,
"az2": 0,
"capEventState": 0,
"lightEventState": 0,
"shorted": False,
"zmod": None,
"thermostat": None,
"playback": None,
"postBackInterval": 600,
"rev": 159,
"version1": 2,
"freqOffset": -9,
"freqCalApplied": 6595,
"reviveEvery": 4,
"oorGrace": 2,
"tempBL": None,
"capBL": None,
"luxBL": None,
"LBTh": 2.67,
"enLBN": True,
"txpwr": 26,
"rssiMode": True,
"ds18": True,
"v2flag": 16,
"batteryRemaining": 0.99
}
61 changes: 61 additions & 0 deletions test/test_outdoor_probe.py
@@ -0,0 +1,61 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

"""Module with tests for outdoor probe sensor in wirelesstags platform."""

import test.mock as MOCK
import unittest

import wirelesstagpy
import wirelesstagpy.constants as CONST

USERNAME = 'foobar'
PASSWORD = 'deadbeef'


class TestOutdoorProbe(unittest.TestCase):
"""Tests for Outdoor Probe sensors logic."""

def setUp(self):
"""Set up wirelesstags platform module."""
self.platform = wirelesstagpy.WirelessTags(username=USERNAME, password=PASSWORD)
self.tag_outdoor = wirelesstagpy.SensorTag(MOCK.OUTDOOR_PROBE, self.platform)
self.platform._tags["fake-1"] = self.tag_outdoor # pylint: disable=protected-access

def tearDown(self):
"""Clean up after each test."""
self.platform = None
self.tag_outdoor = None

def test_probe_type(self):
"""Test probe type."""
rev = 159
last_bits = self.tag_outdoor.extract_last_bits(rev, 4)
print("last bits {}".format(last_bits))
self.assertEqual(last_bits, 0xF)

rev = 158
last_bits = self.tag_outdoor.extract_last_bits(rev, 4)
self.assertEqual(last_bits, 0xE)

rev = 157
last_bits = self.tag_outdoor.extract_last_bits(rev, 4)
self.assertEqual(last_bits, 0xD)

def test_ambient_tmeperature_sensor(self):
"""Test outdoor probe ambient sensor."""
self.assertEqual(self.tag_outdoor.outdoor_probe_has_ambient_temperature, True)

sensor = self.tag_outdoor.sensor[CONST.SENSOR_AMBIENT_TEMPERATURE]
self.assertIsNotNone(sensor)
self.assertEqual(sensor.value, 23.43)

self.assertIn('ambient temp:', str(self.tag_outdoor))

def test_supported_sensor_types(self):
"""Test allowed outdoor probe sensor types generation."""
allowed = self.tag_outdoor.allowed_sensor_types
self.assertIn(CONST.SENSOR_AMBIENT_TEMPERATURE, allowed)
self.assertIn(CONST.SENSOR_TEMPERATURE, allowed)
self.assertNotIn(CONST.SENSOR_HUMIDITY, allowed)
self.assertNotIn(CONST.SENSOR_LIGHT, allowed)
7 changes: 4 additions & 3 deletions wirelesstagpy/constants.py
Expand Up @@ -4,8 +4,8 @@
"""WirelessTags constants."""

MAJOR_VERSION = 0
MINOR_VERSION = 6
PATCH_VERSION = 1
MINOR_VERSION = 7
PATCH_VERSION = 0

__version__ = '{}.{}.{}'.format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION)

Expand Down Expand Up @@ -41,7 +41,7 @@
WIRELESSTAG_TYPE_ALSPRO = 26
WIRELESSTAG_TYPE_WATER = 32
# Reed/RH (52)???
# Outdoor Probe/Thermocouple 42
WIRELESSTAG_TYPE_OUTDOOR_PROBE = 42
WIRELESSTAG_TYPE_PIR = 72
WIRELESSTAG_TYPE_WEMO_DEVICE = 82

Expand Down Expand Up @@ -78,6 +78,7 @@

# supported sensor types
SENSOR_TEMPERATURE = 'temperature'
SENSOR_AMBIENT_TEMPERATURE = 'ambient_temperature'
SENSOR_HUMIDITY = 'humidity'
SENSOR_MOISTURE = 'moisture'
SENSOR_LIGHT = 'light'
Expand Down
9 changes: 7 additions & 2 deletions wirelesstagpy/sensor.py
Expand Up @@ -7,7 +7,7 @@
Single tag contains multiple number of sensors such as
temperature, motion, moisture, humidity or light.
Copyrights: (c) 2018 Sergiy Maysak, see LICENSE file for details
Copyrights: (c) 2018-2021 Sergiy Maysak, see LICENSE file for details
Creation Date: 8/30/2018
"""

Expand All @@ -23,6 +23,11 @@
'attr': 'temperature',
'update_attr': 'temp'
},
CONST.SENSOR_AMBIENT_TEMPERATURE: {
'unit': '°C',
'attr': 'ambient_temperature',
'update_attr': 'cap'
},
CONST.SENSOR_HUMIDITY: {
'unit': '%',
'attr': 'humidity',
Expand All @@ -34,7 +39,7 @@
'update_attr': 'cap'
},
CONST.SENSOR_LIGHT: {
'unit': 'lux',
'unit': 'lx',
'attr': 'light',
'update_attr': 'lux'
}
Expand Down
87 changes: 83 additions & 4 deletions wirelesstagpy/sensortag.py
Expand Up @@ -6,7 +6,7 @@
Tags could of different types with different set of active working attributes.
Copyrights: (c) 2018 Sergiy Maysak, see LICENSE file for details
Copyrights: (c) 2018-2021 Sergiy Maysak, see LICENSE file for details
Creation Date: 3/7/2018
"""

Expand Down Expand Up @@ -234,6 +234,73 @@ def is_battery_low(self) -> bool:
"""Return True if detected low battery level."""
return self.battery_volts <= self.low_battery_threshold

@property
def ambient_temperature(self):
"""Return ambient temperature value. Outdoor Probe only."""
return self.humidity

@property
def revision(self) -> int:
"""Return revision of sensortag hardware."""
return self._info['rev']

@property
def has_sen0227(self) -> bool:
"""Return if SEN0227 is connected."""
return self._info['shorted']

@property
def has_ds18(self) -> bool:
"""Return if ds18 is connected."""
return self._info['ds18']

@property
def product_version(self) -> int:
"""Return product variation variation."""
return self.extract_last_bits(self.revision, 4)

@property
def outdoor_probe_has_ambient_temperature(self) -> bool:
"""Return if outdoor probe tag has ambient temperature."""
# .rev & 0xF: shows which product it is
# 0xD: Outdoor Probe Basic.
# Accepts DS18B20 (read tip temperature in .temperature only)
# or SEN0227 (read tip temperature in .temperature and tip humidity in .cap)
# 0xF: Outdoor Probe Thermocouple.
# Accepts DS18B20 (read tip temperature in .temperature and ambient temperature in .cap)
# or Thermocouple (read tip temperature in .temperature and ambient temperature in .cap)
# or SEN0227 (read tip temperature in .temperature and tip humidity in .cap)
# .shorted: 1 if SEN0227 is connected
# .ds18: 1 if DS18B20 is connected
probe_ambient_map = {
0xD: False, # 0xD: Outdoor Probe Basic.
0xF: not self.has_sen0227 # 0xF: Outdoor Probe Thermocouple.
}
probe_type = self.product_version
return probe_ambient_map[probe_type] if probe_type in probe_ambient_map else False

@property
def outdoor_probe_has_humidity(self) -> bool:
"""Return if outdoor probe tag tip humidity."""
probe_humidity_map = {
0xD: not self.has_ds18, # 0xD: Outdoor Probe Basic.
0xF: self.has_sen0227 # 0xF: Outdoor Probe Thermocouple.
}
probe_type = self.product_version
return probe_humidity_map[probe_type] if probe_type in probe_humidity_map else True

@staticmethod
def extract_last_bits(integer, amount_of_bits) -> int:
"""Return last bits from integer."""
binary = bin(integer)
binary = binary[2:]

end = len(binary)
start = end - amount_of_bits

sub_str = binary[start: end]
return int(sub_str, 2)

@property
def supported_binary_events_types(self):
"""Return supported by tag binary event types (events with state on or off)."""
Expand Down Expand Up @@ -309,16 +376,26 @@ def allowed_sensor_types(self):
CONST.SENSOR_LIGHT],
CONST.WIRELESSTAG_TYPE_PIR: [
CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_HUMIDITY
],
CONST.SENSOR_HUMIDITY],
CONST.WIRELESSTAG_TYPE_OUTDOOR_PROBE: [
CONST.SENSOR_TEMPERATURE,
CONST.SENSOR_HUMIDITY],
CONST.WIRELESSTAG_TYPE_WEMO_DEVICE: []
}

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

if self.tag_type == CONST.WIRELESSTAG_TYPE_OUTDOOR_PROBE:
if self.outdoor_probe_has_ambient_temperature:
allowed_types.append(CONST.SENSOR_AMBIENT_TEMPERATURE)
if not self.outdoor_probe_has_humidity:
allowed_types.remove(CONST.SENSOR_HUMIDITY)

return allowed_types

def sensor_for_type(self, sensor_type) -> Sensor:
"""Return sensor for specified type or None if not supported by tag."""
if sensor_type not in self.allowed_sensor_types:
Expand Down Expand Up @@ -456,6 +533,8 @@ def __repr__(self):
# ALS Pro (8bit)
elif self.tag_type == CONST.WIRELESSTAG_TYPE_ALSPRO:
return '{} temp: {} humidity: {} lux: {}'.format(self.name, self.temperature, self.humidity, self.light)
elif self.tag_type == CONST.WIRELESSTAG_TYPE_OUTDOOR_PROBE and self.outdoor_probe_has_ambient_temperature:
return '{} temp: {} ambient temp: {}'.format(self.name, self.temperature, self.ambient_temperature)

# use 13-bit tag supports temp/motion/humidity as fallback for everything else
return '{} temp: {} humidity: {}'.format(self.name, self.temperature, self.humidity)

0 comments on commit 4d635c2

Please sign in to comment.