From d3cc972583f1d752682525ed933e5bfcb8db3910 Mon Sep 17 00:00:00 2001 From: yozik04 Date: Wed, 1 Apr 2020 10:42:35 +0300 Subject: [PATCH] #13 A_CYC_EXTR_FAN_BALANCE_BASE and A_CYC_SUPP_FAN_BALANCE_BASE added to settable ints. 2.7 compatible enum method replaced with python 3 Enum class (PROFILE). No code changes in clients required. version 2.5.0 Reformat with black and isort. Some code cleanup. --- setup.py | 51 +++-- tests/decorators.py | 17 +- tests/test_client.py | 187 +++++++++--------- tests/test_fetch_logs.py | 24 +-- tests/test_messages.py | 46 +++-- tests/test_set_temperature.py | 47 ++--- tests/test_vallox_profile.py | 297 +++++++++++++++++------------ tests/test_vallox_temperatures.py | 95 +++++---- vallox_websocket_api/__init__.py | 10 +- vallox_websocket_api/client.py | 220 +++++++++++++-------- vallox_websocket_api/exceptions.py | 3 +- vallox_websocket_api/messages.py | 117 ++++++------ vallox_websocket_api/vallox.py | 149 +++++++++------ 13 files changed, 723 insertions(+), 540 deletions(-) diff --git a/setup.py b/setup.py index 12c183b..64824c5 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ -import sys - from os import path as p try: @@ -9,42 +7,37 @@ def read(filename, parent=None): - parent = (parent or __file__) + parent = parent or __file__ try: with open(p.join(p.dirname(parent), filename)) as f: return f.read() except IOError: - return '' + return "" + setup( - name='vallox_websocket_api', - packages=['vallox_websocket_api'], - version='2.4.0', + name="vallox_websocket_api", + packages=["vallox_websocket_api"], + version="2.5.0", python_requires=">=3.5.1, <4", - description='Vallox WebSocket API', - author='Jevgeni Kiski', - author_email='yozik04@gmail.com', - long_description=read('README.md'), + description="Vallox WebSocket API", + author="Jevgeni Kiski", + author_email="yozik04@gmail.com", + long_description=read("README.md"), long_description_content_type="text/markdown", - url='https://github.com/yozik04/vallox_websocket_api', - license='LGPL 3', - keywords='vallox api', + url="https://github.com/yozik04/vallox_websocket_api", + license="LGPL 3", + keywords="vallox api", classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - ], - install_requires=[ - 'websockets >= 7.0, < 9.0', - 'construct >= 2.9.0, < 3.0.0', + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", ], - setup_requires=['wheel'], - tests_require=[ - 'mock', - 'asynctest', - ] + install_requires=["websockets >= 7.0, < 9.0", "construct >= 2.9.0, < 3.0.0",], + setup_requires=["wheel"], + tests_require=["mock", "asynctest",], ) diff --git a/tests/decorators.py b/tests/decorators.py index 7eb3b57..24a4aa7 100644 --- a/tests/decorators.py +++ b/tests/decorators.py @@ -2,14 +2,15 @@ import websockets from vallox_websocket_api.client import Client + def with_client(func): - @asynctest.patch('websockets.connect') - async def wrapper(cls, connect): - client = Client('127.0.0.1') - instance = connect.return_value + @asynctest.patch("websockets.connect") + async def wrapper(cls, connect): + client = Client("127.0.0.1") + instance = connect.return_value - protocol_mock = asynctest.create_autospec(websockets.WebSocketCommonProtocol) - instance.__aenter__.side_effect = protocol_mock - await func(cls, client, protocol_mock.return_value) + protocol_mock = asynctest.create_autospec(websockets.WebSocketCommonProtocol) + instance.__aenter__.side_effect = protocol_mock + await func(cls, client, protocol_mock.return_value) - return wrapper \ No newline at end of file + return wrapper diff --git a/tests/test_client.py b/tests/test_client.py index 2b427ce..22819a9 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,145 +1,128 @@ import binascii -import asynctest -from tests.decorators import with_client +import asynctest from asynctest import CoroutineMock - +from tests.decorators import with_client +from vallox_websocket_api.exceptions import ValloxWebsocketException from websockets.exceptions import InvalidMessage -from vallox_websocket_api.exceptions import ValloxWebsocketException class TestClient(asynctest.TestCase): - @with_client - async def testFetchMetric(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') + @with_client + async def testFetchMetric(self, client, ws): + ws.recv.return_value = binascii.unhexlify( + "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) - result = await client.fetch_metric('A_CYC_ENABLED') + result = await client.fetch_metric("A_CYC_ENABLED") - self.assertEqual(1, result) + self.assertEqual(1, result) - ws.send.assert_called_once_with(binascii.unhexlify('0300f6000000f900')) + ws.send.assert_called_once_with(binascii.unhexlify("0300f6000000f900")) - @with_client - async def testSetTempValue(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetTempValue(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - await client.set_values({ - 'A_CYC_BOOST_AIR_TEMP_TARGET': '19' - }) + await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19"}) - ws.send.assert_called_once_with(binascii.unhexlify('0400f90022501f723ec3')) + ws.send.assert_called_once_with(binascii.unhexlify("0400f90022501f723ec3")) - @with_client - async def testSetTempValueFraction(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetTempValueFraction(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - await client.set_values({ - 'A_CYC_BOOST_AIR_TEMP_TARGET': '19.1' - }) + await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19.1"}) - ws.send.assert_called_once_with(binascii.unhexlify('0400f9002250297248c3')) + ws.send.assert_called_once_with(binascii.unhexlify("0400f9002250297248c3")) - @with_client - async def testSetTempValueFractionRounding1(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetTempValueFractionRounding1(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - await client.set_values({ - 'A_CYC_BOOST_AIR_TEMP_TARGET': '19.145' - }) + await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "19.145"}) - ws.send.assert_called_once_with(binascii.unhexlify('0400f9002250297248c3')) + ws.send.assert_called_once_with(binascii.unhexlify("0400f9002250297248c3")) - @with_client - async def testSetTempValueFractionRounding2(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetTempValueFractionRounding2(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - await client.set_values({ - 'A_CYC_BOOST_AIR_TEMP_TARGET': '18.991' - }) + await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "18.991"}) - ws.send.assert_called_once_with(binascii.unhexlify('0400f90022501f723ec3')) + ws.send.assert_called_once_with(binascii.unhexlify("0400f90022501f723ec3")) - @with_client - async def testSetValue(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetValue(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - await client.set_values({ - 'A_CYC_STATE': 0, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 0 - }) + await client.set_values( + { + "A_CYC_STATE": 0, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 0, + } + ) - ws.send.assert_called_once_with(binascii.unhexlify('0a00f900011200000412000005120000061200001349')) + ws.send.assert_called_once_with( + binascii.unhexlify("0a00f900011200000412000005120000061200001349") + ) - @with_client - async def testSetAssertion(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetAssertion(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - with self.assertRaises(AssertionError) as context: - await client.set_values({ - 'A_CYC_BOOST_TIMER': '11.2' - }) + with self.assertRaises(AssertionError): + await client.set_values({"A_CYC_BOOST_TIMER": "11.2"}) - with self.assertRaises(ValueError) as context: - await client.set_values({ - 'A_CYC_BOOST_AIR_TEMP_TARGET': '11.a' - }) + with self.assertRaises(ValueError): + await client.set_values({"A_CYC_BOOST_AIR_TEMP_TARGET": "11.a"}) - with self.assertRaises(AssertionError) as context: - await client.set_values({ - 'A_CYC_BOOST_SPEED_SETTING': '11.2' - }) + with self.assertRaises(AssertionError): + await client.set_values({"A_CYC_BOOST_SPEED_SETTING": "11.2"}) - with self.assertRaises(AssertionError) as context: - await client.set_values({ - 'A_CYC_FIREPLACE_SUPP_FAN': '11.2' - }) + with self.assertRaises(AssertionError): + await client.set_values({"A_CYC_FIREPLACE_SUPP_FAN": "11.2"}) - @with_client - async def testSetMissing(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetMissing(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - with self.assertRaises(AttributeError) as context: - await client.set_values({ - 'A_CYC_BOOSTER': 10 - }) + with self.assertRaises(AttributeError): + await client.set_values({"A_CYC_BOOSTER": 10}) - @with_client - async def testSetUnsettable(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetUnsettable(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - with self.assertRaises(AttributeError) as context: - await client.set_values({ - 'A_CYC_RH_VALUE': 22 - }) + with self.assertRaises(AttributeError): + await client.set_values({"A_CYC_RH_VALUE": 22}) - @with_client - async def testSetNewSettableAddressByName(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetNewSettableAddressByName(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - client.set_settable_address('A_CYC_RH_VALUE', int) + client.set_settable_address("A_CYC_RH_VALUE", int) - await client.set_values({ - 'A_CYC_RH_VALUE': 22 - }) + await client.set_values({"A_CYC_RH_VALUE": 22}) - @with_client - async def testSetNewSettableAddressByAddress(self, client, ws): - ws.recv.return_value = binascii.unhexlify('0200f500f700') + @with_client + async def testSetNewSettableAddressByAddress(self, client, ws): + ws.recv.return_value = binascii.unhexlify("0200f500f700") - client.set_settable_address(4363, int) + client.set_settable_address(4363, int) - await client.set_values({ - 'A_CYC_RH_VALUE': 22 - }) + await client.set_values({"A_CYC_RH_VALUE": 22}) - @with_client - async def testSetNewSettableAddressByAddressException(self, client, ws): - ws.recv.side_effect = CoroutineMock(side_effect=InvalidMessage()) + @with_client + async def testSetNewSettableAddressByAddressException(self, client, ws): + ws.recv.side_effect = CoroutineMock(side_effect=InvalidMessage()) - client.set_settable_address(4363, int) + client.set_settable_address(4363, int) - await self.assertAsyncRaisesRegex(ValloxWebsocketException, "Websocket handshake failed", client.set_values({ - 'A_CYC_RH_VALUE': 22 - })) \ No newline at end of file + await self.assertAsyncRaisesRegex( + ValloxWebsocketException, + "Websocket handshake failed", + client.set_values({"A_CYC_RH_VALUE": 22}), + ) diff --git a/tests/test_fetch_logs.py b/tests/test_fetch_logs.py index 40e24a5..cc74538 100644 --- a/tests/test_fetch_logs.py +++ b/tests/test_fetch_logs.py @@ -4,14 +4,13 @@ import asynctest import websockets - from vallox_websocket_api import Vallox def with_vallox(func): - @asynctest.patch('websockets.connect') + @asynctest.patch("websockets.connect") async def wrapper(cls, connect): - client = Vallox('127.0.0.1') + client = Vallox("127.0.0.1") client.set_values = asynctest.CoroutineMock() instance = connect.return_value @@ -33,8 +32,8 @@ async def testFetchRawLogs(self, client, ws): :return: """ - resp1 = b'\x03\x00\xf3\x00\x06\x00\xfc\x00' - with open(path.join(path.dirname(__file__), 'log_data.bin'), 'rb') as f: + resp1 = b"\x03\x00\xf3\x00\x06\x00\xfc\x00" + with open(path.join(path.dirname(__file__), "log_data.bin"), "rb") as f: resp2 = f.read() ws.recv.side_effect = [resp1, resp2] @@ -46,9 +45,12 @@ async def testFetchRawLogs(self, client, ws): self.assertEqual(1308, len(data[0])) self.assertEqual(8192, len(data[1])) - self.assertEqual(data[0][0], { - 'id': 0, - 'name': 'extract_air_temp', - 'date': datetime.datetime(2019, 7, 21, 10, 18), - 'value': 24.1 - }) + self.assertEqual( + data[0][0], + { + "id": 0, + "name": "extract_air_temp", + "date": datetime.datetime(2019, 7, 21, 10, 18), + "value": 24.1, + }, + ) diff --git a/tests/test_messages.py b/tests/test_messages.py index e8a31f4..33f0b78 100644 --- a/tests/test_messages.py +++ b/tests/test_messages.py @@ -2,10 +2,11 @@ import struct from unittest import TestCase -from vallox_websocket_api.client import calculate_offset, to_celcius +from vallox_websocket_api.client import calculate_offset, to_celsius from vallox_websocket_api.constants import vlxDevConstants -from vallox_websocket_api.messages import ReadTableRequest, WriteMessageRequest, LogReadRequest, ReadTableResponse, \ - LogReadResponse1 +from vallox_websocket_api.messages import (LogReadRequest, LogReadResponse1, + ReadTableRequest, ReadTableResponse, + WriteMessageRequest) class TestMessages(TestCase): @@ -17,16 +18,22 @@ def test_read_table_message_request(self): self.assertEqual(expected, res) def test_write_message_request(self): - expected = struct.pack('HHHHH', 4, 249, 20508, 29215, 49976) - - res = WriteMessageRequest.build({"fields": {"value": { - "items": [ - { - "address": vlxDevConstants.A_CYC_HOME_AIR_TEMP_TARGET, - "value": 29215 + expected = struct.pack("HHHHH", 4, 249, 20508, 29215, 49976) + + res = WriteMessageRequest.build( + { + "fields": { + "value": { + "items": [ + { + "address": vlxDevConstants.A_CYC_HOME_AIR_TEMP_TARGET, + "value": 29215, + } + ] + } } - ] - }}}) + } + ) self.assertEqual(expected, res) @@ -39,12 +46,21 @@ def test_log_read_message(self): def test_read_table_message_response(self): response = binascii.unhexlify( - '0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') + "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) data = ReadTableResponse.parse(response) - self.assertEqual(20.0, - to_celcius(data[calculate_offset(vlxDevConstants.__dict__['A_CYC_HOME_AIR_TEMP_TARGET'])])) + self.assertEqual( + 20.0, + to_celsius( + data[ + calculate_offset( + vlxDevConstants.__dict__["A_CYC_HOME_AIR_TEMP_TARGET"] + ) + ] + ), + ) def test_log_read_response(self): response = struct.pack("HHHH", 3, 243, 6, 252) diff --git a/tests/test_set_temperature.py b/tests/test_set_temperature.py index b30de98..e954e58 100644 --- a/tests/test_set_temperature.py +++ b/tests/test_set_temperature.py @@ -1,36 +1,37 @@ import struct -import asynctest +import asynctest from tests.decorators import with_client + class TestClient(asynctest.TestCase): - # [4, 249, 20508, 29215, 49976] - @with_client - async def testSetHomeAirTempTargetRawInt(self, client, ws): - ws.recv.return_value = b'' + # [4, 249, 20508, 29215, 49976] + @with_client + async def testSetHomeAirTempTargetRawInt(self, client, ws): + ws.recv.return_value = b"" - await client.set_values({ - 'A_CYC_HOME_AIR_TEMP_TARGET': 19 - }) + await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": 19}) - ws.send.assert_called_once_with(struct.pack('HHHHH', 4, 249, 20508, 29215, 49976)) + ws.send.assert_called_once_with( + struct.pack("HHHHH", 4, 249, 20508, 29215, 49976) + ) - @with_client - async def testSetHomeAirTempTargetRawFloat(self, client, ws): - ws.recv.return_value = b'' + @with_client + async def testSetHomeAirTempTargetRawFloat(self, client, ws): + ws.recv.return_value = b"" - await client.set_values({ - 'A_CYC_HOME_AIR_TEMP_TARGET': 19.0 - }) + await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": 19.0}) - ws.send.assert_called_once_with(struct.pack('HHHHH', 4, 249, 20508, 29215, 49976)) + ws.send.assert_called_once_with( + struct.pack("HHHHH", 4, 249, 20508, 29215, 49976) + ) - @with_client - async def testSetHomeAirTempTargetRawString(self, client, ws): - ws.recv.return_value = b'' + @with_client + async def testSetHomeAirTempTargetRawString(self, client, ws): + ws.recv.return_value = b"" - await client.set_values({ - 'A_CYC_HOME_AIR_TEMP_TARGET': '19' - }) + await client.set_values({"A_CYC_HOME_AIR_TEMP_TARGET": "19"}) - ws.send.assert_called_once_with(struct.pack('HHHHH', 4, 249, 20508, 29215, 49976)) \ No newline at end of file + ws.send.assert_called_once_with( + struct.pack("HHHHH", 4, 249, 20508, 29215, 49976) + ) diff --git a/tests/test_vallox_profile.py b/tests/test_vallox_profile.py index d5593bb..ec2ad95 100644 --- a/tests/test_vallox_profile.py +++ b/tests/test_vallox_profile.py @@ -1,127 +1,184 @@ import asynctest - -from vallox_websocket_api import Vallox, PROFILE +from vallox_websocket_api import PROFILE, Vallox class TestValloxSetProfile(asynctest.TestCase): - def setUp(self): - self.client = Vallox('127.0.0.1') - - self.client.set_values = asynctest.CoroutineMock() - - async def checkSetProfile(self, profile, set_values_dict): - await self.client.set_profile(profile) - - self.client.set_values.assert_called_once_with(set_values_dict) - - async def testSetProfileHome(self): - await self.checkSetProfile(PROFILE.HOME, {'A_CYC_STATE': '0', - 'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '0'}) - - async def testSetProfileAway(self): - await self.checkSetProfile(PROFILE.AWAY, {'A_CYC_STATE': '1', - 'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '0'}) - - async def testSetProfileBoost(self): - self.client.fetch_metric = asynctest.CoroutineMock(return_value=30) - - await self.checkSetProfile(PROFILE.BOOST, {'A_CYC_BOOST_TIMER': '30', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '0'}) - - self.client.fetch_metric.assert_called_once_with('A_CYC_BOOST_TIME') - - async def testSetProfileFireplace(self): - self.client.fetch_metric = asynctest.CoroutineMock(return_value=30) - - await self.checkSetProfile(PROFILE.FIREPLACE, {'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '30', - 'A_CYC_EXTRA_TIMER': '0'}) - - self.client.fetch_metric.assert_called_once_with('A_CYC_FIREPLACE_TIME') - - async def testSetProfileExtra(self): - self.client.fetch_metric = asynctest.CoroutineMock(return_value=30) - - await self.checkSetProfile(PROFILE.EXTRA, {'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '30'}) + def setUp(self): + self.client = Vallox("127.0.0.1") + + self.client.set_values = asynctest.CoroutineMock() + + async def checkSetProfile(self, profile, set_values_dict): + await self.client.set_profile(profile) + + self.client.set_values.assert_called_once_with(set_values_dict) + + async def testSetProfileHome(self): + await self.checkSetProfile( + PROFILE.HOME, + { + "A_CYC_STATE": "0", + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "0", + }, + ) + + async def testSetProfileAway(self): + await self.checkSetProfile( + PROFILE.AWAY, + { + "A_CYC_STATE": "1", + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "0", + }, + ) + + async def testSetProfileBoost(self): + self.client.fetch_metric = asynctest.CoroutineMock(return_value=30) + + await self.checkSetProfile( + PROFILE.BOOST, + { + "A_CYC_BOOST_TIMER": "30", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "0", + }, + ) + + self.client.fetch_metric.assert_called_once_with("A_CYC_BOOST_TIME") + + async def testSetProfileFireplace(self): + self.client.fetch_metric = asynctest.CoroutineMock(return_value=30) + + await self.checkSetProfile( + PROFILE.FIREPLACE, + { + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "30", + "A_CYC_EXTRA_TIMER": "0", + }, + ) + + self.client.fetch_metric.assert_called_once_with("A_CYC_FIREPLACE_TIME") + + async def testSetProfileExtra(self): + self.client.fetch_metric = asynctest.CoroutineMock(return_value=30) + + await self.checkSetProfile( + PROFILE.EXTRA, + { + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "30", + }, + ) + + self.client.fetch_metric.assert_called_once_with("A_CYC_EXTRA_TIME") - self.client.fetch_metric.assert_called_once_with('A_CYC_EXTRA_TIME') class TestValloxGetProfile(asynctest.TestCase): - def setUp(self): - self.client = Vallox('127.0.0.1') - - async def checkGetProfile(self, fetch_metrics_result, expected_profile): - self.client.fetch_metrics = asynctest.CoroutineMock(return_value=fetch_metrics_result) - - self.assertEqual(await self.client.get_profile(), expected_profile) - - self.client.fetch_metrics.assert_called_once_with(['A_CYC_STATE', 'A_CYC_BOOST_TIMER', - 'A_CYC_FIREPLACE_TIMER', 'A_CYC_EXTRA_TIMER']) - - async def testGetProfileHome(self): - await self.checkGetProfile({ - 'A_CYC_STATE': 0, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 0 - }, PROFILE.HOME) - - async def testGetProfileAway(self): - await self.checkGetProfile({ - 'A_CYC_STATE': 1, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 0 - }, PROFILE.AWAY) - - async def testGetProfileBoost(self): - await self.checkGetProfile({ - 'A_CYC_STATE': 0, - 'A_CYC_BOOST_TIMER': 30, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 0 - }, PROFILE.BOOST) - - await self.checkGetProfile({ - 'A_CYC_STATE': 1, - 'A_CYC_BOOST_TIMER': 30, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 0 - }, PROFILE.BOOST) - - async def testGetProfileFireplace(self): - await self.checkGetProfile({ - 'A_CYC_STATE': 0, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 30, - 'A_CYC_EXTRA_TIMER': 0 - }, PROFILE.FIREPLACE) - - await self.checkGetProfile({ - 'A_CYC_STATE': 1, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 30, - 'A_CYC_EXTRA_TIMER': 0 - }, PROFILE.FIREPLACE) - - async def testGetProfileExtra(self): - await self.checkGetProfile({ - 'A_CYC_STATE': 0, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 30 - }, PROFILE.EXTRA) - - await self.checkGetProfile({ - 'A_CYC_STATE': 1, - 'A_CYC_BOOST_TIMER': 0, - 'A_CYC_FIREPLACE_TIMER': 0, - 'A_CYC_EXTRA_TIMER': 30 - }, PROFILE.EXTRA) \ No newline at end of file + def setUp(self): + self.client = Vallox("127.0.0.1") + + async def checkGetProfile(self, fetch_metrics_result, expected_profile): + self.client.fetch_metrics = asynctest.CoroutineMock( + return_value=fetch_metrics_result + ) + + self.assertEqual(await self.client.get_profile(), expected_profile) + + self.client.fetch_metrics.assert_called_once_with( + [ + "A_CYC_STATE", + "A_CYC_BOOST_TIMER", + "A_CYC_FIREPLACE_TIMER", + "A_CYC_EXTRA_TIMER", + ] + ) + + async def testGetProfileHome(self): + await self.checkGetProfile( + { + "A_CYC_STATE": 0, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 0, + }, + PROFILE.HOME, + ) + + async def testGetProfileAway(self): + await self.checkGetProfile( + { + "A_CYC_STATE": 1, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 0, + }, + PROFILE.AWAY, + ) + + async def testGetProfileBoost(self): + await self.checkGetProfile( + { + "A_CYC_STATE": 0, + "A_CYC_BOOST_TIMER": 30, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 0, + }, + PROFILE.BOOST, + ) + + await self.checkGetProfile( + { + "A_CYC_STATE": 1, + "A_CYC_BOOST_TIMER": 30, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 0, + }, + PROFILE.BOOST, + ) + + async def testGetProfileFireplace(self): + await self.checkGetProfile( + { + "A_CYC_STATE": 0, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 30, + "A_CYC_EXTRA_TIMER": 0, + }, + PROFILE.FIREPLACE, + ) + + await self.checkGetProfile( + { + "A_CYC_STATE": 1, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 30, + "A_CYC_EXTRA_TIMER": 0, + }, + PROFILE.FIREPLACE, + ) + + async def testGetProfileExtra(self): + await self.checkGetProfile( + { + "A_CYC_STATE": 0, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 30, + }, + PROFILE.EXTRA, + ) + + await self.checkGetProfile( + { + "A_CYC_STATE": 1, + "A_CYC_BOOST_TIMER": 0, + "A_CYC_FIREPLACE_TIMER": 0, + "A_CYC_EXTRA_TIMER": 30, + }, + PROFILE.EXTRA, + ) diff --git a/tests/test_vallox_temperatures.py b/tests/test_vallox_temperatures.py index bbdc33a..9689694 100644 --- a/tests/test_vallox_temperatures.py +++ b/tests/test_vallox_temperatures.py @@ -1,64 +1,81 @@ -import mock import binascii import struct -import asynctest -from vallox_websocket_api import Vallox, PROFILE +import asynctest from tests.decorators import with_client +from vallox_websocket_api import PROFILE, Vallox class TestValloxSetTemperature(asynctest.TestCase): - def setUp(self): - self.client = Vallox('127.0.0.1') + def setUp(self): + self.client = Vallox("127.0.0.1") - self.client.set_values = asynctest.CoroutineMock() + self.client.set_values = asynctest.CoroutineMock() - async def checkSetTemperature(self, profile, temperature, set_values_dict): - await self.client.set_temperature(profile, temperature) + async def checkSetTemperature(self, profile, temperature, set_values_dict): + await self.client.set_temperature(profile, temperature) - self.client.set_values.assert_called_once_with(set_values_dict) + self.client.set_values.assert_called_once_with(set_values_dict) - async def testSetTemperatureHome(self): - await self.checkSetTemperature(PROFILE.HOME, 19, {'A_CYC_HOME_AIR_TEMP_TARGET': 19}) + async def testSetTemperatureHome(self): + await self.checkSetTemperature( + PROFILE.HOME, 19, {"A_CYC_HOME_AIR_TEMP_TARGET": 19} + ) - async def testSetTemperatureAway(self): - await self.checkSetTemperature(PROFILE.AWAY, 19, {'A_CYC_AWAY_AIR_TEMP_TARGET': 19}) + async def testSetTemperatureAway(self): + await self.checkSetTemperature( + PROFILE.AWAY, 19, {"A_CYC_AWAY_AIR_TEMP_TARGET": 19} + ) - async def testSetTemperatureBoost(self): - await self.checkSetTemperature(PROFILE.BOOST, 19, {'A_CYC_BOOST_AIR_TEMP_TARGET': 19}) + async def testSetTemperatureBoost(self): + await self.checkSetTemperature( + PROFILE.BOOST, 19, {"A_CYC_BOOST_AIR_TEMP_TARGET": 19} + ) - async def testSetTemperatureWrong(self): - with self.assertRaises(AttributeError) as context: - await self.checkSetTemperature(PROFILE.FIREPLACE, 19, {'A_CYC_FIREPLACE_AIR_TEMP_TARGET': 19}) + async def testSetTemperatureWrong(self): + with self.assertRaises(AttributeError): + await self.checkSetTemperature( + PROFILE.FIREPLACE, 19, {"A_CYC_FIREPLACE_AIR_TEMP_TARGET": 19} + ) class TestValloxGetTemperature(asynctest.TestCase): - def setUp(self): - self.client = Vallox('127.0.0.1') - - async def checkGetTemperatureForProfile(self, fetch_metrics_result, profile, expected_temperature): - self.client.fetch_metrics = asynctest.CoroutineMock(return_value=fetch_metrics_result) - - self.assertEqual(await self.client.get_temperature(profile), expected_temperature) - - self.client.fetch_metrics.assert_called_once_with(list(fetch_metrics_result.keys())) - - async def testGetTemperatureForProfileHome(self): - await self.checkGetTemperatureForProfile({ - 'A_CYC_HOME_AIR_TEMP_TARGET': 19 - }, PROFILE.HOME, 19) - - @with_client - async def testFetchMetric(self, client, ws): - """ + def setUp(self): + self.client = Vallox("127.0.0.1") + + async def checkGetTemperatureForProfile( + self, fetch_metrics_result, profile, expected_temperature + ): + self.client.fetch_metrics = asynctest.CoroutineMock( + return_value=fetch_metrics_result + ) + + self.assertEqual( + await self.client.get_temperature(profile), expected_temperature + ) + + self.client.fetch_metrics.assert_called_once_with( + list(fetch_metrics_result.keys()) + ) + + async def testGetTemperatureForProfileHome(self): + await self.checkGetTemperatureForProfile( + {"A_CYC_HOME_AIR_TEMP_TARGET": 19}, PROFILE.HOME, 19 + ) + + @with_client + async def testFetchMetric(self, client, ws): + """ IoQueue.KItemTypeFetch = 3 VlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES; = 246 item.value = 0; checksum = 249 Uint16Array(4) [3, 246, 0, 249] """ - ws.recv.return_value = binascii.unhexlify('0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') + ws.recv.return_value = binascii.unhexlify( + "0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) - self.assertEqual(20.0, await client.fetch_metric('A_CYC_HOME_AIR_TEMP_TARGET')) + self.assertEqual(20.0, await client.fetch_metric("A_CYC_HOME_AIR_TEMP_TARGET")) - ws.send.assert_called_once_with(struct.pack( "HHHH", 3, 246, 0, 249)) \ No newline at end of file + ws.send.assert_called_once_with(struct.pack("HHHH", 3, 246, 0, 249)) diff --git a/vallox_websocket_api/__init__.py b/vallox_websocket_api/__init__.py index f3990f3..3757fe3 100644 --- a/vallox_websocket_api/__init__.py +++ b/vallox_websocket_api/__init__.py @@ -1,5 +1,11 @@ from .client import Client -from .vallox import Vallox, PROFILE from .exceptions import ValloxApiException, ValloxWebsocketException +from .vallox import PROFILE, Vallox -__all__ = ["Client", "Vallox", "PROFILE", "ValloxApiException", "ValloxWebsocketException"] +__all__ = [ + "Client", + "Vallox", + "PROFILE", + "ValloxApiException", + "ValloxWebsocketException", +] diff --git a/vallox_websocket_api/client.py b/vallox_websocket_api/client.py index e5f1996..e276f2e 100644 --- a/vallox_websocket_api/client.py +++ b/vallox_websocket_api/client.py @@ -1,12 +1,12 @@ -import datetime import re import websockets from .constants import vlxDevConstants, vlxOffsetObject from .exceptions import ValloxWebsocketException -from .messages import LogReadRequest, WriteMessageRequest, ReadTableRequest, ReadTableResponse, LogReadResponse1, \ - LogReadResponse2 +from .messages import (LogReadRequest, LogReadResponse1, LogReadResponse2, + ReadTableRequest, ReadTableResponse, + WriteMessageRequest) KPageSize = 65536 @@ -14,49 +14,110 @@ def calculate_offset(aIndex): offset = 0 - if ((aIndex > vlxDevConstants.RANGE_START_g_cyclone_general_info) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_general_info)): + if (aIndex > vlxDevConstants.RANGE_START_g_cyclone_general_info) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_general_info + ): offset = aIndex + 1 - elif ((aIndex > vlxDevConstants.RANGE_START_g_typhoon_general_info) and ( - aIndex <= vlxDevConstants.RANGE_END_g_typhoon_general_info)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_typhoon_general_info + vlxOffsetObject.CYC_NUM_OF_GENERAL_INFO - elif ((aIndex > vlxDevConstants.RANGE_START_g_cyclone_hw_state) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_hw_state)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_hw_state + vlxOffsetObject.CYC_NUM_OF_GENERAL_TYP_INFO - elif ((aIndex > vlxDevConstants.RANGE_START_g_cyclone_sw_state) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_sw_state)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_sw_state + vlxOffsetObject.CYC_NUM_OF_HW_STATES - elif ( - (aIndex > vlxDevConstants.RANGE_START_g_cyclone_time) and (aIndex <= vlxDevConstants.RANGE_END_g_cyclone_time)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_time + vlxOffsetObject.CYC_NUM_OF_SW_STATES - elif ( - (aIndex > vlxDevConstants.RANGE_START_g_cyclone_output) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_output)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_output + vlxOffsetObject.CYC_NUM_OF_TIME_ELEMENTS - elif ((aIndex > vlxDevConstants.RANGE_START_g_cyclone_input) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_input)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_input + vlxOffsetObject.CYC_NUM_OF_OUTPUTS - elif ( - (aIndex > vlxDevConstants.RANGE_START_g_cyclone_config) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_config)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_config + vlxOffsetObject.CYC_NUM_OF_INPUTS - elif ((aIndex > vlxDevConstants.RANGE_START_g_cyclone_settings) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_settings)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_settings + vlxOffsetObject.CYC_NUM_OF_CONFIGS - elif ((aIndex > vlxDevConstants.RANGE_START_g_typhoon_settings) and ( - aIndex <= vlxDevConstants.RANGE_END_g_typhoon_settings)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_typhoon_settings + vlxOffsetObject.CYC_NUM_OF_CYC_SETTINGS - elif ((aIndex > vlxDevConstants.RANGE_START_g_self_test) and (aIndex <= vlxDevConstants.RANGE_END_g_self_test)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_self_test + vlxOffsetObject.CYC_NUM_OF_TYP_SETTINGS - elif ((aIndex > vlxDevConstants.RANGE_START_g_faults) and (aIndex <= vlxDevConstants.RANGE_END_g_faults)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_faults + vlxOffsetObject.CYC_NUM_OF_SELF_TESTS - elif ((aIndex > vlxDevConstants.RANGE_START_g_cyclone_weekly_schedule) and ( - aIndex <= vlxDevConstants.RANGE_END_g_cyclone_weekly_schedule)): - offset = (aIndex) - vlxDevConstants.RANGE_START_g_cyclone_weekly_schedule + vlxOffsetObject.CYC_NUM_OF_FAULTS + elif (aIndex > vlxDevConstants.RANGE_START_g_typhoon_general_info) and ( + aIndex <= vlxDevConstants.RANGE_END_g_typhoon_general_info + ): + offset = ( + aIndex + - vlxDevConstants.RANGE_START_g_typhoon_general_info + + vlxOffsetObject.CYC_NUM_OF_GENERAL_INFO + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_hw_state) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_hw_state + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_hw_state + + vlxOffsetObject.CYC_NUM_OF_GENERAL_TYP_INFO + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_sw_state) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_sw_state + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_sw_state + + vlxOffsetObject.CYC_NUM_OF_HW_STATES + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_time) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_time + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_time + + vlxOffsetObject.CYC_NUM_OF_SW_STATES + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_output) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_output + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_output + + vlxOffsetObject.CYC_NUM_OF_TIME_ELEMENTS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_input) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_input + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_input + + vlxOffsetObject.CYC_NUM_OF_OUTPUTS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_config) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_config + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_config + + vlxOffsetObject.CYC_NUM_OF_INPUTS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_settings) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_settings + ): + offset = ( + (aIndex) # noqa + - vlxDevConstants.RANGE_START_g_cyclone_settings + + vlxOffsetObject.CYC_NUM_OF_CONFIGS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_typhoon_settings) and ( + aIndex <= vlxDevConstants.RANGE_END_g_typhoon_settings + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_typhoon_settings + + vlxOffsetObject.CYC_NUM_OF_CYC_SETTINGS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_self_test) and ( + aIndex <= vlxDevConstants.RANGE_END_g_self_test + ): + offset = ( + (aIndex) # noqa + - vlxDevConstants.RANGE_START_g_self_test + + vlxOffsetObject.CYC_NUM_OF_TYP_SETTINGS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_faults) and ( + aIndex <= vlxDevConstants.RANGE_END_g_faults + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_faults + + vlxOffsetObject.CYC_NUM_OF_SELF_TESTS + ) + elif (aIndex > vlxDevConstants.RANGE_START_g_cyclone_weekly_schedule) and ( + aIndex <= vlxDevConstants.RANGE_END_g_cyclone_weekly_schedule + ): + offset = ( + (aIndex) + - vlxDevConstants.RANGE_START_g_cyclone_weekly_schedule + + vlxOffsetObject.CYC_NUM_OF_FAULTS + ) return offset - 1 -def to_celcius(value: int) -> float: +def to_celsius(value: int) -> float: return round(value / 100.0 - 273.15, 1) @@ -70,25 +131,29 @@ def to_kelvin(value: float) -> int: 2: "outdoor_air_temp", 3: "supply_air_temp", 4: "co2", - 5: "humidity" + 5: "humidity", } class Client: SETTABLE_INT_VALS = { - re.compile('^A_CYC_STATE$'), - re.compile('^A_CYC_(?:HOME|AWAY|BOOST|EXTRA)_AIR_TEMP_TARGET$'), - re.compile('^A_CYC_(?:HOME|AWAY|BOOST)_SPEED_SETTING$'), - re.compile('^A_CYC_(?:BOOST|FIREPLACE|EXTRA)_TIMER$'), - re.compile('^A_CYC_(?:FIREPLACE|EXTRA)_(?:EXTR|SUPP)_FAN$') + re.compile("^A_CYC_STATE$"), + re.compile("^A_CYC_(?:HOME|AWAY|BOOST|EXTRA)_AIR_TEMP_TARGET$"), + re.compile("^A_CYC_(?:HOME|AWAY|BOOST)_SPEED_SETTING$"), + re.compile("^A_CYC_(?:BOOST|FIREPLACE|EXTRA)_TIMER$"), + re.compile("^A_CYC_(?:FIREPLACE|EXTRA)_(?:EXTR|SUPP)_FAN$"), + re.compile("^A_CYC_(?:EXTR|SUPP)_FAN_BALANCE_BASE$"), } _settable_addresses = None def get_settable_addresses(self): if not self._settable_addresses: - self._settable_addresses = dict((v, int) for k, v in vlxDevConstants.__dict__.items() if - any(r.match(k) for r in self.SETTABLE_INT_VALS)) + self._settable_addresses = dict( + (v, int) + for k, v in vlxDevConstants.__dict__.items() + if any(r.match(k) for r in self.SETTABLE_INT_VALS) + ) return self._settable_addresses def set_settable_address(self, address, var_type): @@ -106,7 +171,9 @@ def set_settable_address(self, address, var_type): self._settable_addresses[key] = var_type return - raise AttributeError("Unable to add address '%s' to settable list" % str(address)) + raise AttributeError( + "Unable to add address '%s' to settable list" % str(address) + ) def __init__(self, ip_address): self.ip_address = ip_address @@ -114,22 +181,24 @@ def __init__(self, ip_address): def _decode_pair(self, key, value): try: address = int(getattr(vlxDevConstants, key, key)) - except ValueError as e: + except ValueError: raise AttributeError("%s setting does not exist" % key) - if '_TEMP_' in key: + if "_TEMP_" in key: value = to_kelvin(float(value)) try: raw_value = int(value) - except ValueError as e: + except ValueError: raw_value = float(value) addresses = self.get_settable_addresses() try: required_type = addresses[address] - except KeyError as e: + except KeyError: raise AttributeError("%s setting is not settable" % key) - assert type(raw_value) == required_type, "%s(%d) key needs to be an %s, but %s passed" % ( - key, address, required_type.__name__, type(raw_value).__name__) + assert type(raw_value) == required_type, ( + "%s(%d) key needs to be an %s, but %s passed" + % (key, address, required_type.__name__, type(raw_value).__name__) + ) return address, raw_value @@ -144,15 +213,15 @@ async def _websocket_request(self, payload, read_packets=1): results.append(r) return results[0] if read_packets == 1 else results except websockets.InvalidHandshake as e: - raise ValloxWebsocketException('Websocket handshake failed') from e + raise ValloxWebsocketException("Websocket handshake failed") from e except websockets.InvalidURI as e: - raise ValloxWebsocketException('Websocket invalid URI') from e + raise ValloxWebsocketException("Websocket invalid URI") from e except websockets.PayloadTooBig as e: - raise ValloxWebsocketException('Websocket payload too big') from e + raise ValloxWebsocketException("Websocket payload too big") from e except websockets.InvalidState as e: - raise ValloxWebsocketException('Websocket invalid state') from e + raise ValloxWebsocketException("Websocket invalid state") from e except websockets.WebSocketProtocolError as e: - raise ValloxWebsocketException('Websocket protocol error') from e + raise ValloxWebsocketException("Websocket protocol error") from e async def fetch_metrics(self, metric_keys=None): metrics = {} @@ -167,8 +236,8 @@ async def fetch_metrics(self, metric_keys=None): for key in metric_keys: value = data[calculate_offset(vlxDevConstants.__dict__[key])] - if '_TEMP_' in key: - value = to_celcius(value) + if "_TEMP_" in key: + value = to_celsius(value) metrics[key] = value @@ -182,9 +251,12 @@ async def fetch_raw_logs(self): expected_total_len = KPageSize * page_count result1_len = len(result[1]) - assert expected_total_len == result1_len, "Response is not full %d/%d" % (result1_len, expected_total_len) + assert expected_total_len == result1_len, "Response is not full %d/%d" % ( + result1_len, + expected_total_len, + ) - pages = (result[1][i:i + KPageSize] for i in range(0, result1_len, KPageSize)) + pages = (result[1][i : i + KPageSize] for i in range(0, result1_len, KPageSize)) series = [] for page in pages: @@ -196,10 +268,10 @@ async def fetch_raw_logs(self): "id": int(cell.id), "name": name, "date": cell.date, - "value": cell.value + "value": cell.value, } - if '_temp' in name: - point['value'] = to_celcius(cell.value) + if "_temp" in name: + point["value"] = to_celsius(cell.value) points.append(point) @@ -210,16 +282,14 @@ async def fetch_raw_logs(self): async def fetch_metric(self, metric_key): return (await self.fetch_metrics([metric_key])).get(metric_key, None) - async def set_values(self, dict): + async def set_values(self, dict_): items = [] - for key, value in dict.items(): + for key, value in dict_.items(): address, raw_value = self._decode_pair(key, value) items.append({"address": address, "value": raw_value}) - items.sort(key=lambda x: x['address']) - payload = WriteMessageRequest.build({"fields": {"value": { - "items": items - }}}) + items.sort(key=lambda x: x["address"]) + payload = WriteMessageRequest.build({"fields": {"value": {"items": items}}}) await self._websocket_request(payload) return True diff --git a/vallox_websocket_api/exceptions.py b/vallox_websocket_api/exceptions.py index 0f6e5ab..4f71137 100644 --- a/vallox_websocket_api/exceptions.py +++ b/vallox_websocket_api/exceptions.py @@ -1,7 +1,8 @@ class ValloxApiException(Exception): pass + class ValloxWebsocketException(ValloxApiException): """ Is raised on any websocket error - """ \ No newline at end of file + """ diff --git a/vallox_websocket_api/messages.py b/vallox_websocket_api/messages.py index 63e50a0..0f3ed2f 100644 --- a/vallox_websocket_api/messages.py +++ b/vallox_websocket_api/messages.py @@ -1,7 +1,8 @@ import datetime -from construct import Struct, Const, Int16ul, Int16ub, Int8ub, this, len_, Rebuild, Checksum, RawCopy, \ - GreedyRange, Adapter, Bytes, Enum +from construct import (Adapter, Bytes, Checksum, Const, Enum, GreedyRange, + Int8ub, Int16ub, Int16ul, RawCopy, Rebuild, Struct, + len_, this) from .constants import vlxDevConstants @@ -11,76 +12,82 @@ def checksum_16(data): for i in range(len(data) // 2): c = c + (data[i * 2 + 1] << 8) + data[i * 2] - return c & 0xffff + return c & 0xFFFF -WriteCommand = Struct( - "address" / Int16ul, - "value" / Int16ul -) +WriteCommand = Struct("address" / Int16ul, "value" / Int16ul) WriteMessageRequest = Struct( - "fields" / RawCopy(Struct( - "length" / Rebuild(Int16ul, len_(this.items) * WriteCommand.sizeof() // Int16ul.sizeof() + 2), - "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_WRITE_DATA, Int16ul), - "items" / WriteCommand[len_(this.items)], - )), - "checksum" / Checksum(Int16ul, - lambda data: checksum_16(data), - this.fields.data) + "fields" + / RawCopy( + Struct( + "length" + / Rebuild( + Int16ul, + len_(this.items) * WriteCommand.sizeof() // Int16ul.sizeof() + 2, + ), + "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_WRITE_DATA, Int16ul), + "items" / WriteCommand[len_(this.items)], + ) + ), + "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), ) ReadTableResponse = GreedyRange(Int16ub) ReadTableRequest = Struct( - "fields" / RawCopy(Struct( - "length" / Const(3, Int16ul), - "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES, Int16ul), - "items" / Const(0, Int16ul), - )), - "checksum" / Checksum(Int16ul, - lambda data: checksum_16(data), - this.fields.data) + "fields" + / RawCopy( + Struct( + "length" / Const(3, Int16ul), + "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES, Int16ul), + "items" / Const(0, Int16ul), + ) + ), + "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), ) LogReadRequest = Struct( - "fields" / RawCopy(Struct( - "length" / Const(2, Int16ul), - "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW, Int16ul) - )), - "checksum" / Checksum(Int16ul, - lambda data: checksum_16(data), - this.fields.data) + "fields" + / RawCopy( + Struct( + "length" / Const(2, Int16ul), + "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW, Int16ul), + ) + ), + "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), ) LogReadResponse1 = Struct( - "fields" / RawCopy(Struct( - "length" / Const(3, Int16ul), - "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW, Int16ul), - "pages" / Int16ul, - )), - "checksum" / Checksum(Int16ul, - lambda data: checksum_16(data), - this.fields.data) + "fields" + / RawCopy( + Struct( + "length" / Const(3, Int16ul), + "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW, Int16ul), + "pages" / Int16ul, + ) + ), + "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), ) class DateAdapter(Adapter): def _decode(self, obj, context, path): - return datetime.datetime(year=2000 + obj[4], month=obj[3], day=obj[2], hour=obj[1], minute=obj[0]) - - -LogItemId = Enum(Int8ub, - extract_air_temp=0, - exhaust_air_temp=1, - outdoor_air_temp=2, - supply_air_temp=3, - co2=4, - humidity=5 - ) - -LogReadResponse2 = GreedyRange(Struct( - "id" / LogItemId, - "date" / DateAdapter(Bytes(5)), - "value" / Int16ul -)).compile() + return datetime.datetime( + year=2000 + obj[4], month=obj[3], day=obj[2], hour=obj[1], minute=obj[0] + ) + + +LogItemId = Enum( + Int8ub, + extract_air_temp=0, + exhaust_air_temp=1, + outdoor_air_temp=2, + supply_air_temp=3, + co2=4, + humidity=5, +) + +LogReadResponse2 = GreedyRange( + Struct("id" / LogItemId, "date" / DateAdapter(Bytes(5)), "value" / Int16ul) +).compile() diff --git a/vallox_websocket_api/vallox.py b/vallox_websocket_api/vallox.py index 025ca52..7ad0c55 100644 --- a/vallox_websocket_api/vallox.py +++ b/vallox_websocket_api/vallox.py @@ -1,49 +1,54 @@ -from .constants import vlxDevConstants -from .client import Client - import logging +from enum import Enum +from .client import Client -def enum(*sequential, **named): - enums = dict(zip(sequential, range(len(sequential))), **named) - reverse = dict((value, key) for key, value in enums.items()) - enums['reverse_mapping'] = reverse - return type('Enum', (), enums) -PROFILE = enum( - NONE = 0, - HOME = 1, - AWAY = 2, - BOOST = 3, - FIREPLACE = 4, - EXTRA = 5 -) +class PROFILE(Enum): + NONE = 0 + HOME = 1 + AWAY = 2 + BOOST = 3 + FIREPLACE = 4 + EXTRA = 5 + MAP = { - "temperature": { - PROFILE.HOME: 'A_CYC_HOME_AIR_TEMP_TARGET', - PROFILE.AWAY: 'A_CYC_AWAY_AIR_TEMP_TARGET', - PROFILE.BOOST: 'A_CYC_BOOST_AIR_TEMP_TARGET' - } + "temperature": { + PROFILE.HOME: "A_CYC_HOME_AIR_TEMP_TARGET", + PROFILE.AWAY: "A_CYC_AWAY_AIR_TEMP_TARGET", + PROFILE.BOOST: "A_CYC_BOOST_AIR_TEMP_TARGET", + } } + class Vallox(Client): async def get_profile(self): """Returns the profile of the fan :returns: One of PROFILE.* values or PROFILE.NONE if unknown """ - s = await self.fetch_metrics(['A_CYC_STATE','A_CYC_BOOST_TIMER', - 'A_CYC_FIREPLACE_TIMER','A_CYC_EXTRA_TIMER']) - - if s['A_CYC_BOOST_TIMER'] > 0: return PROFILE.BOOST - if s['A_CYC_FIREPLACE_TIMER'] > 0: return PROFILE.FIREPLACE - if s['A_CYC_EXTRA_TIMER'] > 0: return PROFILE.EXTRA - if s['A_CYC_STATE'] == 1: return PROFILE.AWAY - elif s['A_CYC_STATE'] == 0: return PROFILE.HOME + s = await self.fetch_metrics( + [ + "A_CYC_STATE", + "A_CYC_BOOST_TIMER", + "A_CYC_FIREPLACE_TIMER", + "A_CYC_EXTRA_TIMER", + ] + ) + + if s["A_CYC_BOOST_TIMER"] > 0: + return PROFILE.BOOST + if s["A_CYC_FIREPLACE_TIMER"] > 0: + return PROFILE.FIREPLACE + if s["A_CYC_EXTRA_TIMER"] > 0: + return PROFILE.EXTRA + if s["A_CYC_STATE"] == 1: + return PROFILE.AWAY + elif s["A_CYC_STATE"] == 0: + return PROFILE.HOME return PROFILE.NONE - async def set_profile(self, profile, duration=None): set_duration = None if duration is not None and 0 <= int(duration) <= 65535: @@ -56,60 +61,84 @@ async def set_profile(self, profile, duration=None): :duration: timeout in minutes for the FIREPLACE, BOOST and EXTRA profiles """ - #duration: None means default configured setting. 65535 means no time out + # duration: None means default configured setting. 65535 means no time out if profile == PROFILE.HOME: - logging.info('Setting unit to HOME profile') - await self.set_values({'A_CYC_STATE': '0', - 'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '0'}) + logging.info("Setting unit to HOME profile") + await self.set_values( + { + "A_CYC_STATE": "0", + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "0", + } + ) elif profile == PROFILE.AWAY: - logging.info('Setting unit to AWAY profile') - await self.set_values({'A_CYC_STATE': '1', - 'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '0'}) + logging.info("Setting unit to AWAY profile") + await self.set_values( + { + "A_CYC_STATE": "1", + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "0", + } + ) elif profile == PROFILE.FIREPLACE: if set_duration is not None: dur = str(set_duration) else: - dur = str(await self.fetch_metric('A_CYC_FIREPLACE_TIME')) - logging.info('Setting unit to FIREPLACE profile for %s minutes', dur) - await self.set_values({'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': dur, - 'A_CYC_EXTRA_TIMER': '0'}) + dur = str(await self.fetch_metric("A_CYC_FIREPLACE_TIME")) + logging.info("Setting unit to FIREPLACE profile for %s minutes", dur) + await self.set_values( + { + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": dur, + "A_CYC_EXTRA_TIMER": "0", + } + ) elif profile == PROFILE.BOOST: if set_duration is not None: dur = str(set_duration) else: - dur = str(await self.fetch_metric('A_CYC_BOOST_TIME')) - logging.info('Setting unit to BOOST profile for %s minutes', dur) - await self.set_values({'A_CYC_BOOST_TIMER': dur, - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': '0'}) + dur = str(await self.fetch_metric("A_CYC_BOOST_TIME")) + logging.info("Setting unit to BOOST profile for %s minutes", dur) + await self.set_values( + { + "A_CYC_BOOST_TIMER": dur, + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": "0", + } + ) elif profile == PROFILE.EXTRA: if set_duration is not None: dur = str(set_duration) else: - dur = str(await self.fetch_metric('A_CYC_EXTRA_TIME')) - logging.info('Setting unit to EXTRA profile for %s minutes', dur) - await self.set_values({'A_CYC_BOOST_TIMER': '0', - 'A_CYC_FIREPLACE_TIMER': '0', - 'A_CYC_EXTRA_TIMER': dur}) + dur = str(await self.fetch_metric("A_CYC_EXTRA_TIME")) + logging.info("Setting unit to EXTRA profile for %s minutes", dur) + await self.set_values( + { + "A_CYC_BOOST_TIMER": "0", + "A_CYC_FIREPLACE_TIMER": "0", + "A_CYC_EXTRA_TIMER": dur, + } + ) async def get_temperature(self, profile): try: setting = MAP["temperature"][profile] - except KeyError as e: - raise AttributeError("Temperature is not gettable for this profile: " + str(profile)) + except KeyError: + raise AttributeError( + "Temperature is not gettable for this profile: " + str(profile) + ) return await self.fetch_metric(setting) async def set_temperature(self, profile, temperature): try: setting = MAP["temperature"][profile] - except KeyError as e: - raise AttributeError("Temperature is not settable for this profile: " + str(profile)) + except KeyError: + raise AttributeError( + "Temperature is not settable for this profile: " + str(profile) + ) await self.set_values({setting: temperature})