1
0
mirror of https://github.com/no2chem/wideq.git synced 2025-05-18 08:10:17 -07:00

Merge pull request #26 from boralyl/feature/add-additional-types

Adds additional types returned when monitoring an LG dryer.
This commit is contained in:
Adrian Sampson 2019-07-01 11:40:44 -04:00 committed by GitHub
commit 1e8e51c334
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 125 additions and 11 deletions

110
tests/test_client.py Normal file
View File

@ -0,0 +1,110 @@
import unittest
from wideq.client import (
BitValue, EnumValue, ModelInfo, RangeValue, ReferenceValue)
DATA = {
'Value': {
'AntiBacterial': {
'default': '0',
'label': '@WM_DRY27_BUTTON_ANTI_BACTERIAL_W',
'option': {
'0': '@CP_OFF_EN_W',
'1': '@CP_ON_EN_W'
},
'type': 'Enum'
},
'Course': {
'option': ['Course'],
'type': 'Reference',
},
'Initial_Time_H': {
'default': 0,
'option': {'max': 24, 'min': 0},
'type': 'Range'
},
'Option1': {
'default': '0',
'option': [
{
'default': '0',
'length': 1,
'startbit': 0,
'value': 'ChildLock'
},
{
'default': '0',
'length': 1,
'startbit': 1,
'value': 'ReduceStatic'
},
{
'default': '0',
'length': 1,
'startbit': 2,
'value': 'EasyIron'
},
{
'default': '0',
'length': 1,
'startbit': 3,
'value': 'DampDrySingal'
},
{
'default': '0',
'length': 1,
'startbit': 4,
'value': 'WrinkleCare'
},
{
'default': '0',
'length': 1,
'startbit': 7,
'value': 'AntiBacterial'
}
],
'type': 'Bit'
},
'Unexpected': {'type': 'Unexpected'},
},
}
class ModelInfoTest(unittest.TestCase):
def setUp(self):
super().setUp()
self.model_info = ModelInfo(DATA)
def test_value_enum(self):
actual = self.model_info.value('AntiBacterial')
expected = EnumValue({'0': '@CP_OFF_EN_W', '1': '@CP_ON_EN_W'})
self.assertEqual(expected, actual)
def test_value_range(self):
actual = self.model_info.value('Initial_Time_H')
expected = RangeValue(min=0, max=24, step=1)
self.assertEqual(expected, actual)
def test_value_bit(self):
actual = self.model_info.value('Option1')
expected = BitValue({
0: 'ChildLock',
1: 'ReduceStatic',
2: 'EasyIron',
3: 'DampDrySingal',
4: 'WrinkleCare',
7: 'AntiBacterial',
})
self.assertEqual(expected, actual)
def test_value_reference(self):
actual = self.model_info.value('Course')
expected = ReferenceValue('Course')
self.assertEqual(expected, actual)
def test_value_unsupported(self):
with self.assertRaisesRegex(
ValueError, 'unsupported value type Unexpected'):
self.model_info.value('Unexpected')

View File

@ -293,8 +293,10 @@ class DeviceInfo(object):
return requests.get(self.model_info_url).json()
BitValue = namedtuple('BitValue', ['options'])
EnumValue = namedtuple('EnumValue', ['options'])
RangeValue = namedtuple('RangeValue', ['min', 'max', 'step'])
ReferenceValue = namedtuple('ReferenceValue', ['reference'])
class ModelInfo(object):
@ -304,31 +306,38 @@ class ModelInfo(object):
def __init__(self, data):
self.data = data
def value(self, name):
def value(self, name: str):
"""Look up information about a value.
Return either an `EnumValue` or a `RangeValue`.
:param name: The name to look up.
:returns: One of (`BitValue`, `EnumValue`, `RangeValue`,
`ReferenceValue`).
:raises ValueError: If an unsupported type is encountered.
"""
d = self.data['Value'][name]
if d['type'] in ('Enum', 'enum'):
return EnumValue(d['option'])
elif d['type'] == 'Range':
return RangeValue(
d['option']['min'], d['option']['max'], d['option']['step']
d['option']['min'], d['option']['max'],
d['option'].get('step', 1)
)
elif d['type'].lower() == 'bit':
bit_values = {opt['startbit']: opt['value'] for opt in d['option']}
return BitValue(bit_values)
elif d['type'].lower() == 'reference':
return ReferenceValue(d['option'][0])
else:
assert False, "unsupported value type {}".format(d['type'])
raise ValueError("unsupported value type {}".format(d['type']))
def default(self, name):
"""Get the default value, if it exists, for a given value.
"""
return self.data['Value'][name]['default']
def enum_value(self, key, name):
"""Look up the encoded value for a friendly enum name.
"""
options = self.value(key).options
options_inv = {v: k for k, v in options.items()} # Invert the map.
return options_inv[name]
@ -336,7 +345,6 @@ class ModelInfo(object):
def enum_name(self, key, value):
"""Look up the friendly enum name for an encoded value.
"""
options = self.value(key).options
return options[value]
@ -344,13 +352,11 @@ class ModelInfo(object):
def binary_monitor_data(self):
"""Check that type of monitoring is BINARY(BYTE).
"""
return self.data['Monitoring']['type'] == 'BINARY(BYTE)'
def decode_monitor_binary(self, data):
"""Decode binary encoded status data.
"""
decoded = {}
for item in self.data['Monitoring']['protocol']:
key = item['value']
@ -363,12 +369,10 @@ class ModelInfo(object):
def decode_monitor_json(self, data):
"""Decode a bytestring that encodes JSON status data."""
return json.loads(data.decode('utf8'))
def decode_monitor(self, data):
"""Decode status data."""
if self.binary_monitor_data:
return self.decode_monitor_binary(data)
else: