diff --git a/changelog.md b/changelog.md index 43f43cedd..9474a6564 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,8 @@ - renamed "PhysicalAddress" to "IndividualAddress" - Telegram: `group_address` renamed to `destination_address`, to prepare support for other APCI services and add `source_address` +- Telegram: remove `Telegram.telegramtype` and replace with payload object derived from `xknx.telegram.apci.APCI`. +- CEMIFrame: remove `CEMIFrame.cmd`, which can be derived from `CEMIFrame.payload`. - Farewell Travis CI; Welcome Github Actions! ## 0.15.6 Bugfix for StateUpater 2020-11-26 diff --git a/examples/example_tunnel.py b/examples/example_tunnel.py index c54505685..979adde32 100644 --- a/examples/example_tunnel.py +++ b/examples/example_tunnel.py @@ -5,6 +5,7 @@ from xknx.dpt import DPTBinary from xknx.io import GatewayScanner, Tunnel from xknx.telegram import GroupAddress, IndividualAddress, Telegram +from xknx.telegram.apci import GroupValueWrite async def main(): @@ -38,11 +39,17 @@ async def main(): await tunnel.connect() await tunnel.send_telegram( - Telegram(destination_address=GroupAddress("1/0/15"), payload=DPTBinary(1)) + Telegram( + destination_address=GroupAddress("1/0/15"), + payload=GroupValueWrite(DPTBinary(1)), + ) ) await asyncio.sleep(2) await tunnel.send_telegram( - Telegram(destination_address=GroupAddress("1/0/15"), payload=DPTBinary(0)) + Telegram( + destination_address=GroupAddress("1/0/15"), + payload=GroupValueWrite(DPTBinary(0)), + ) ) await asyncio.sleep(2) diff --git a/home-assistant-plugin/custom_components/xknx/__init__.py b/home-assistant-plugin/custom_components/xknx/__init__.py index 4b82e1d31..44142025b 100644 --- a/home-assistant-plugin/custom_components/xknx/__init__.py +++ b/home-assistant-plugin/custom_components/xknx/__init__.py @@ -14,6 +14,7 @@ ConnectionType, ) from xknx.telegram import AddressFilter, GroupAddress, Telegram +from xknx.telegram.apci import GroupValueResponse, GroupValueWrite from homeassistant.const import ( CONF_ENTITY_ID, @@ -322,14 +323,20 @@ def async_create_exposures(self): async def telegram_received_cb(self, telegram): """Call invoked after a KNX telegram was received.""" + data = None + + # Not all telegrams have serializable data. + if isinstance(telegram, (GroupValueWrite, GroupValueResponse)): + data = telegram.dpt.value + self.hass.bus.async_fire( "knx_event", { - "data": telegram.payload.value, + "data": data, "destination": str(telegram.destination_address), "direction": telegram.direction.value, "source": str(telegram.source_address), - "telegramtype": telegram.telegramtype.value, + "telegramtype": telegram.payload.__class__.__name__, }, ) diff --git a/test/core_tests/telegram_queue_test.py b/test/core_tests/telegram_queue_test.py index 4460ec296..6ab5f98cc 100644 --- a/test/core_tests/telegram_queue_test.py +++ b/test/core_tests/telegram_queue_test.py @@ -7,6 +7,7 @@ from xknx.dpt import DPTBinary from xknx.exceptions import CouldNotParseTelegram from xknx.telegram import AddressFilter, GroupAddress, Telegram, TelegramDirection +from xknx.telegram.apci import GroupValueWrite class AsyncMock(MagicMock): @@ -40,7 +41,7 @@ def test_start(self): telegram_in = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(xknx.telegram_queue.start()) @@ -75,14 +76,12 @@ async def async_none(): telegram_in = Telegram( direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), - destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) telegram_out = Telegram( direction=TelegramDirection.OUTGOING, - payload=DPTBinary(1), - destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(xknx.telegram_queue.start()) @@ -120,7 +119,7 @@ async def async_telegram_received_cb(device): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete( xknx.telegram_queue.process_telegram_incoming(telegram) @@ -144,7 +143,7 @@ def test_unregister(self): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete( xknx.telegram_queue.process_telegram_incoming(telegram) @@ -170,7 +169,7 @@ def test_process_to_device(self, devices_by_ga_mock): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete( xknx.telegram_queue.process_telegram_incoming(telegram) @@ -193,7 +192,7 @@ def test_process_to_callback(self, devices_process): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete( xknx.telegram_queue.process_telegram_incoming(telegram) @@ -215,7 +214,7 @@ def test_outgoing(self, logger_warning_mock, if_mock): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.OUTGOING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) # log a warning if there is no KNXIP interface instanciated @@ -249,7 +248,7 @@ async def process_exception(): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) @@ -279,12 +278,12 @@ def test_process_all_telegrams( telegram_in = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) telegram_out = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.OUTGOING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram_in) @@ -314,7 +313,7 @@ async def async_telegram_received_cb(device): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) self.loop.run_until_complete(xknx.telegram_queue._process_all_telegrams()) @@ -343,7 +342,7 @@ async def async_telegram_received_cb(device): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) self.loop.run_until_complete(xknx.telegram_queue._process_all_telegrams()) @@ -372,7 +371,7 @@ async def async_telegram_received_cb(device): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) xknx.telegrams.put_nowait(telegram) self.loop.run_until_complete(xknx.telegram_queue._process_all_telegrams()) diff --git a/test/core_tests/value_reader_test.py b/test/core_tests/value_reader_test.py index 901739227..5f5ee7971 100644 --- a/test/core_tests/value_reader_test.py +++ b/test/core_tests/value_reader_test.py @@ -6,7 +6,8 @@ from xknx import XKNX from xknx.core import ValueReader from xknx.dpt import DPTBinary -from xknx.telegram import GroupAddress, Telegram, TelegramDirection, TelegramType +from xknx.telegram import GroupAddress, Telegram, TelegramDirection +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class TestValueReader(unittest.TestCase): @@ -28,9 +29,8 @@ def test_value_reader_read_success(self, timeout_mock): test_group_address = GroupAddress("0/0/0") response_telegram = Telegram( destination_address=test_group_address, - telegramtype=TelegramType.GROUP_RESPONSE, direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueResponse(DPTBinary(1)), ) value_reader = ValueReader(xknx, test_group_address) @@ -94,8 +94,7 @@ def test_value_reader_send_group_read(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("0/0/0"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("0/0/0"), payload=GroupValueRead() ), ) @@ -105,27 +104,23 @@ def test_value_reader_telegram_received(self): test_group_address = GroupAddress("0/0/0") expected_telegram_1 = Telegram( destination_address=test_group_address, - telegramtype=TelegramType.GROUP_RESPONSE, direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueResponse(DPTBinary(1)), ) expected_telegram_2 = Telegram( destination_address=test_group_address, - telegramtype=TelegramType.GROUP_WRITE, direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) telegram_wrong_address = Telegram( destination_address=GroupAddress("0/0/1"), - telegramtype=TelegramType.GROUP_RESPONSE, direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueResponse(DPTBinary(1)), ) telegram_wrong_type = Telegram( destination_address=test_group_address, - telegramtype=TelegramType.GROUP_READ, direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueRead(), ) value_reader = ValueReader(xknx, test_group_address) diff --git a/test/devices_tests/binary_sensor_test.py b/test/devices_tests/binary_sensor_test.py index 33649836f..58a336fa5 100644 --- a/test/devices_tests/binary_sensor_test.py +++ b/test/devices_tests/binary_sensor_test.py @@ -7,7 +7,8 @@ from xknx.devices import Action, BinarySensor, Switch from xknx.dpt import DPTArray, DPTBinary from xknx.exceptions import CouldNotParseTelegram -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueResponse, GroupValueWrite class AsyncMock(MagicMock): @@ -38,14 +39,16 @@ def test_process(self): self.assertEqual(binaryinput.state, None) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(binaryinput.process(telegram_on)) self.assertEqual(binaryinput.state, True) telegram_off = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.loop.run_until_complete(binaryinput.process(telegram_off)) self.assertEqual(binaryinput.state, False) @@ -54,7 +57,8 @@ def test_process(self): self.assertEqual(binaryinput2.state, None) telegram_off2 = Telegram( - destination_address=GroupAddress("1/2/4"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/4"), + payload=GroupValueWrite(DPTBinary(0)), ) self.loop.run_until_complete(binaryinput2.process(telegram_off2)) self.assertEqual(binaryinput2.state, False) @@ -67,14 +71,16 @@ def test_process_invert(self): self.assertEqual(bs_invert.state, None) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.loop.run_until_complete(bs_invert.process(telegram_on)) self.assertEqual(bs_invert.state, True) telegram_off = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(bs_invert.process(telegram_off)) self.assertEqual(bs_invert.state, False) @@ -92,7 +98,8 @@ def test_process_reset_after(self): device_updated_cb=async_after_update_callback, ) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(binaryinput.process(telegram_on)) @@ -131,7 +138,8 @@ def test_process_action(self): self.assertEqual(switch.state, None) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(binary_sensor.process(telegram_on)) # process outgoing telegram from queue @@ -141,7 +149,8 @@ def test_process_action(self): self.assertEqual(switch.state, True) telegram_off = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.loop.run_until_complete(binary_sensor.process(telegram_off)) self.loop.run_until_complete(switch.process(xknx.telegrams.get_nowait())) @@ -164,7 +173,8 @@ def test_process_action_ignore_internal_state(self): self.assertEqual(switch.state, None) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) with patch("time.time") as mock_time, patch( @@ -197,7 +207,8 @@ def test_process_wrong_payload(self): xknx = XKNX() binary_sensor = BinarySensor(xknx, "Warning", group_address_state="1/2/3") telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x1, 0x2, 0x3))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(binary_sensor.process(telegram)) @@ -245,7 +256,8 @@ def test_process_callback(self): switch.register_device_updated_cb(async_after_update_callback) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(switch.process(telegram)) # no _context_task started because ignore_internal_state is False @@ -273,7 +285,8 @@ def test_process_callback_ignore_internal_state(self): switch.register_device_updated_cb(async_after_update_callback) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.assertEqual(switch.counter, 0) @@ -315,7 +328,8 @@ def test_process_callback_ignore_internal_state_no_counter(self): switch.register_device_updated_cb(async_after_update_callback) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(switch.process(telegram)) # no _context_task started because context_timeout is False @@ -342,12 +356,14 @@ def test_process_group_value_response(self): switch.register_device_updated_cb(async_after_update_callback) write_telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) response_telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTBinary(1), - telegramtype=TelegramType.GROUP_RESPONSE, + payload=GroupValueResponse( + DPTBinary(1), + ), ) self.assertIsNone(switch.state) # initial GroupValueResponse changes state and runs callback diff --git a/test/devices_tests/climate_test.py b/test/devices_tests/climate_test.py index 7d9587b52..19dcfeecb 100644 --- a/test/devices_tests/climate_test.py +++ b/test/devices_tests/climate_test.py @@ -19,7 +19,8 @@ ) from xknx.dpt.dpt_hvac_mode import HVACControllerMode from xknx.exceptions import CouldNotParseTelegram, DeviceIllegalValue -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueWrite DPT_20102_MODES = [ HVACOperationMode.AUTO, @@ -233,7 +234,7 @@ async def async_after_update_callback(device): telegram = Telegram( destination_address=GroupAddress("1/2/1"), - payload=DPTArray(DPTTemperature.to_knx(23)), + payload=GroupValueWrite(DPTArray(DPTTemperature.to_knx(23))), ) self.loop.run_until_complete(climate.process(telegram)) after_update_callback.assert_called_with(climate) @@ -241,7 +242,7 @@ async def async_after_update_callback(device): telegram = Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPTTemperature.to_knx(23)), + payload=GroupValueWrite(DPTArray(DPTTemperature.to_knx(23))), ) self.loop.run_until_complete(climate.process(telegram)) after_update_callback.assert_called_with(climate) @@ -249,7 +250,7 @@ async def async_after_update_callback(device): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray(DPTValue1Count.to_knx(-4)), + payload=GroupValueWrite(DPTArray(DPTValue1Count.to_knx(-4))), ) self.loop.run_until_complete(climate.process(telegram)) after_update_callback.assert_called_with(climate) @@ -276,7 +277,8 @@ async def async_after_update_callback(device): # Note: the climate object processes the telegram, but the cb # is called with the climate_mode object. telegram = Telegram( - destination_address=GroupAddress("1/2/4"), payload=DPTArray(1) + destination_address=GroupAddress("1/2/4"), + payload=GroupValueWrite(DPTArray(1)), ) self.loop.run_until_complete(climate.process(telegram)) after_update_callback.assert_called_with(climate_mode) @@ -303,7 +305,9 @@ def test_set_operation_mode(self): telegram, Telegram( destination_address=GroupAddress("1/2/4"), - payload=DPTArray(DPTHVACMode.to_knx(operation_mode)), + payload=GroupValueWrite( + DPTArray(DPTHVACMode.to_knx(operation_mode)) + ), ), ) @@ -325,7 +329,9 @@ def test_set_controller_operation_mode(self): telegram, Telegram( destination_address=GroupAddress("1/2/4"), - payload=DPTArray(DPTHVACContrMode.to_knx(controller_mode)), + payload=GroupValueWrite( + DPTArray(DPTHVACContrMode.to_knx(controller_mode)) + ), ), ) @@ -367,7 +373,9 @@ def test_set_operation_mode_with_controller_status(self): telegram, Telegram( destination_address=GroupAddress("1/2/4"), - payload=DPTArray(DPTControllerStatus.to_knx(operation_mode)), + payload=GroupValueWrite( + DPTArray(DPTControllerStatus.to_knx(operation_mode)) + ), ), ) @@ -393,15 +401,21 @@ def test_set_operation_mode_with_separate_addresses(self): telegrams.append(xknx.telegrams.get_nowait()) test_telegrams = [ - Telegram(destination_address=GroupAddress("1/2/4"), payload=DPTArray(1)), Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTBinary(False) + destination_address=GroupAddress("1/2/4"), + payload=GroupValueWrite(DPTArray(1)), + ), + Telegram( + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTBinary(False)), ), Telegram( - destination_address=GroupAddress("1/2/6"), payload=DPTBinary(False) + destination_address=GroupAddress("1/2/6"), + payload=GroupValueWrite(DPTBinary(False)), ), Telegram( - destination_address=GroupAddress("1/2/7"), payload=DPTBinary(True) + destination_address=GroupAddress("1/2/7"), + payload=GroupValueWrite(DPTBinary(True)), ), ] @@ -425,7 +439,8 @@ def test_set_heat_cool_binary(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/14"), payload=DPTBinary(True) + destination_address=GroupAddress("1/2/14"), + payload=GroupValueWrite(DPTBinary(True)), ), ) self.loop.run_until_complete( @@ -436,7 +451,8 @@ def test_set_heat_cool_binary(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/14"), payload=DPTBinary(False) + destination_address=GroupAddress("1/2/14"), + payload=GroupValueWrite(DPTBinary(False)), ), ) @@ -533,7 +549,10 @@ def test_target_temperature_up(self): self.assertEqual( _telegram, # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10 - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(30)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(30)), + ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -544,7 +563,7 @@ def test_target_temperature_up(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(23.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(23.00))), ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -556,7 +575,10 @@ def test_target_temperature_up(self): _telegram = xknx.telegrams.get_nowait() self.assertEqual( _telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(40)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(40)), + ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) _telegram = xknx.telegrams.get_nowait() @@ -564,7 +586,7 @@ def test_target_temperature_up(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(24.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(24.00))), ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -576,7 +598,10 @@ def test_target_temperature_up(self): _telegram = xknx.telegrams.get_nowait() self.assertEqual( _telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(35)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(35)), + ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) _telegram = xknx.telegrams.get_nowait() @@ -584,7 +609,7 @@ def test_target_temperature_up(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(23.50)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(23.50))), ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -619,7 +644,10 @@ def test_target_temperature_down(self): self.assertEqual( _telegram, # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10 - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(10)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(10)), + ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -630,7 +658,7 @@ def test_target_temperature_down(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(23.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(23.00))), ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -642,7 +670,10 @@ def test_target_temperature_down(self): _telegram = xknx.telegrams.get_nowait() self.assertEqual( _telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(0xF1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(0xF1)), + ), ) # -15 self.loop.run_until_complete(xknx.devices.process(_telegram)) _telegram = xknx.telegrams.get_nowait() @@ -650,7 +681,7 @@ def test_target_temperature_down(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(20.50)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(20.50))), ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -662,7 +693,10 @@ def test_target_temperature_down(self): _telegram = xknx.telegrams.get_nowait() self.assertEqual( _telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(0xE2)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(0xE2)), + ), ) # -30 self.loop.run_until_complete(xknx.devices.process(_telegram)) _telegram = xknx.telegrams.get_nowait() @@ -670,7 +704,7 @@ def test_target_temperature_down(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(19.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(19.00))), ), ) self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -709,7 +743,10 @@ def test_target_temperature_modified_step(self): self.assertEqual( _telegram, # temperature_step is 0.5 -> payload = setpoint_shift * 2 - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(6)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(6)), + ), ) self.loop.run_until_complete(climate.target_temperature.set(23.00)) @@ -720,7 +757,7 @@ def test_target_temperature_modified_step(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(23.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(23.00))), ), ) self.assertEqual(climate.base_temperature, 20.00) @@ -730,7 +767,10 @@ def test_target_temperature_modified_step(self): self.loop.run_until_complete(xknx.devices.process(_telegram)) self.assertEqual( _telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(8)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(8)), + ), ) _telegram = xknx.telegrams.get_nowait() self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -738,7 +778,7 @@ def test_target_temperature_modified_step(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(24.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(24.00))), ), ) self.assertEqual(climate.target_temperature.value, 24.00) @@ -771,7 +811,7 @@ def test_base_temperature(self): _telegram, Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(21.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(21.00))), ), ) self.assertFalse(climate.initialized_for_setpoint_shift_calculations) @@ -785,7 +825,10 @@ def test_base_temperature(self): self.assertEqual( _telegram, # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10 - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(10)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(10)), + ), ) self.assertTrue(climate.initialized_for_setpoint_shift_calculations) self.assertEqual(climate.base_temperature, 20.00) @@ -799,7 +842,10 @@ def test_base_temperature(self): self.assertEqual( _telegram, # DEFAULT_TEMPERATURE_STEP is 0.1 -> payload = setpoint_shift * 10 - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(20)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(20)), + ), ) _telegram = xknx.telegrams.get_nowait() self.loop.run_until_complete(xknx.devices.process(_telegram)) @@ -826,7 +872,7 @@ def test_target_temperature_step_mode_9002(self): climate.target_temperature.process( Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(20.00)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(20.00))), ) ) ) @@ -841,7 +887,7 @@ def test_target_temperature_step_mode_9002(self): _telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x00, 0x00)), + payload=GroupValueWrite(DPTArray((0x00, 0x00))), ), ) # 0 # - 0.6 °C = 19.4 @@ -853,7 +899,7 @@ def test_target_temperature_step_mode_9002(self): _telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x87, 0xC4)), + payload=GroupValueWrite(DPTArray((0x87, 0xC4))), ), ) # -0.6 # simulate incoming new target temperature for next calculation @@ -861,7 +907,7 @@ def test_target_temperature_step_mode_9002(self): climate.target_temperature.process( Telegram( destination_address=GroupAddress("1/2/2"), - payload=DPTArray(DPT2ByteFloat().to_knx(19.40)), + payload=GroupValueWrite(DPTArray(DPT2ByteFloat().to_knx(19.40))), ) ) ) @@ -874,7 +920,7 @@ def test_target_temperature_step_mode_9002(self): _telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x01, 0x5E)), + payload=GroupValueWrite(DPTArray((0x01, 0x5E))), ), ) # +3.5 @@ -927,11 +973,7 @@ def test_sync(self): self.assertEqual(xknx.telegrams.qsize(), 1) telegram1 = xknx.telegrams.get_nowait() self.assertEqual( - telegram1, - Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, - ), + telegram1, Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()) ) def test_sync_operation_mode(self): @@ -947,11 +989,7 @@ def test_sync_operation_mode(self): self.assertEqual(xknx.telegrams.qsize(), 1) telegram1 = xknx.telegrams.get_nowait() self.assertEqual( - telegram1, - Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, - ), + telegram1, Telegram(GroupAddress("1/2/4"), payload=GroupValueRead()) ) def test_sync_controller_status(self): @@ -967,11 +1005,7 @@ def test_sync_controller_status(self): self.assertEqual(xknx.telegrams.qsize(), 1) telegram1 = xknx.telegrams.get_nowait() self.assertEqual( - telegram1, - Telegram( - destination_address=GroupAddress("1/2/24"), - telegramtype=TelegramType.GROUP_READ, - ), + telegram1, Telegram(GroupAddress("1/2/24"), payload=GroupValueRead()) ) def test_sync_controller_mode(self): @@ -987,11 +1021,7 @@ def test_sync_controller_mode(self): self.assertEqual(xknx.telegrams.qsize(), 1) telegram1 = xknx.telegrams.get_nowait() self.assertEqual( - telegram1, - Telegram( - destination_address=GroupAddress("1/2/14"), - telegramtype=TelegramType.GROUP_READ, - ), + telegram1, Telegram(GroupAddress("1/2/14"), payload=GroupValueRead()) ) def test_sync_operation_mode_state(self): @@ -1013,22 +1043,13 @@ def test_sync_operation_mode_state(self): telegrams = [] for _ in range(3): telegrams.append(xknx.telegrams.get_nowait()) - self.assertSetEqual( - set(telegrams), - { - Telegram( - destination_address=GroupAddress("1/2/5"), - telegramtype=TelegramType.GROUP_READ, - ), - Telegram( - destination_address=GroupAddress("1/2/6"), - telegramtype=TelegramType.GROUP_READ, - ), - Telegram( - destination_address=GroupAddress("1/2/14"), - telegramtype=TelegramType.GROUP_READ, - ), - }, + self.assertListEqual( + telegrams, + [ + Telegram(GroupAddress("1/2/5"), payload=GroupValueRead()), + Telegram(GroupAddress("1/2/6"), payload=GroupValueRead()), + Telegram(GroupAddress("1/2/14"), payload=GroupValueRead()), + ], ) def test_sync_heat_cool(self): @@ -1044,11 +1065,7 @@ def test_sync_heat_cool(self): self.assertEqual(xknx.telegrams.qsize(), 1) telegram1 = xknx.telegrams.get_nowait() self.assertEqual( - telegram1, - Telegram( - destination_address=GroupAddress("1/2/15"), - telegramtype=TelegramType.GROUP_READ, - ), + telegram1, Telegram(GroupAddress("1/2/15"), payload=GroupValueRead()) ) # @@ -1059,8 +1076,10 @@ def test_process_temperature(self): xknx = XKNX() climate = Climate(xknx, "TestClimate", group_address_temperature="1/2/3") - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.payload = DPTArray(DPTTemperature().to_knx(21.34)) + telegram = Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(DPTTemperature().to_knx(21.34))), + ) self.loop.run_until_complete(climate.process(telegram)) self.assertEqual(climate.temperature.value, 21.34) @@ -1074,15 +1093,21 @@ def test_process_operation_mode(self): group_address_controller_status="1/2/3", ) for operation_mode in DPT_20102_MODES: - telegram = Telegram(destination_address=GroupAddress("1/2/5")) - telegram.payload = DPTArray(DPTHVACMode.to_knx(operation_mode)) + telegram = Telegram( + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray(DPTHVACMode.to_knx(operation_mode))), + ) self.loop.run_until_complete(climate_mode.process(telegram)) self.assertEqual(climate_mode.operation_mode, operation_mode) for operation_mode in DPT_20102_MODES: if operation_mode == HVACOperationMode.AUTO: continue - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.payload = DPTArray(DPTControllerStatus.to_knx(operation_mode)) + telegram = Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite( + DPTArray(DPTControllerStatus.to_knx(operation_mode)) + ), + ) self.loop.run_until_complete(climate_mode.process(telegram)) self.assertEqual(climate_mode.operation_mode, operation_mode) @@ -1093,8 +1118,12 @@ def test_process_controller_mode(self): xknx, "TestClimate", group_address_controller_mode="1/2/5" ) for _, controller_mode in DPTHVACContrMode.SUPPORTED_MODES.items(): - telegram = Telegram(destination_address=GroupAddress("1/2/5")) - telegram.payload = DPTArray(DPTHVACContrMode.to_knx(controller_mode)) + telegram = Telegram( + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite( + DPTArray(DPTHVACContrMode.to_knx(controller_mode)) + ), + ) self.loop.run_until_complete(climate_mode.process(telegram)) self.assertEqual(climate_mode.controller_mode, controller_mode) @@ -1108,7 +1137,8 @@ def test_process_controller_status_wrong_payload(self): group_address_controller_status="1/2/3", ) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(climate_mode.process(telegram)) @@ -1123,7 +1153,8 @@ def test_process_controller_status_payload_invalid_length(self): group_address_controller_status="1/2/3", ) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((23, 24)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((23, 24))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(climate_mode.process(telegram)) @@ -1138,7 +1169,8 @@ def test_process_operation_mode_wrong_payload(self): group_address_controller_status="1/2/3", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(climate_mode.process(telegram)) @@ -1153,7 +1185,8 @@ def test_process_operation_mode_payload_invalid_length(self): group_address_controller_status="1/2/3", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24)) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray((23, 24))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(climate_mode.process(telegram)) @@ -1172,8 +1205,10 @@ async def async_after_update_callback(device): climate.register_device_updated_cb(async_after_update_callback) - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.payload = DPTArray(DPTTemperature().to_knx(21.34)) + telegram = Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(DPTTemperature().to_knx(21.34))), + ) self.loop.run_until_complete(climate.process(telegram)) after_update_callback.assert_called_with(climate) @@ -1187,13 +1222,17 @@ def test_process_heat_cool(self): group_address_heat_cool_state="1/2/15", ) - telegram = Telegram(destination_address=GroupAddress("1/2/14")) - telegram.payload = DPTBinary(False) + telegram = Telegram( + destination_address=GroupAddress("1/2/14"), + payload=GroupValueWrite(DPTBinary(False)), + ) self.loop.run_until_complete(climate_mode.process(telegram)) self.assertEqual(climate_mode.controller_mode, HVACControllerMode.COOL) - telegram = Telegram(destination_address=GroupAddress("1/2/14")) - telegram.payload = DPTBinary(True) + telegram = Telegram( + destination_address=GroupAddress("1/2/14"), + payload=GroupValueWrite(DPTBinary(True)), + ) self.loop.run_until_complete(climate_mode.process(telegram)) self.assertEqual(climate_mode.controller_mode, HVACControllerMode.HEAT) @@ -1349,16 +1388,20 @@ def test_process_power_status(self): """Test process / reading telegrams from telegram queue. Test if DPT20.105 controller mode is set correctly.""" xknx = XKNX() climate = Climate(xknx, "TestClimate", group_address_on_off="1/2/2") - telegram = Telegram(destination_address=GroupAddress("1/2/2")) - telegram.payload = DPTBinary(1) + telegram = Telegram( + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(1)), + ) self.loop.run_until_complete(climate.process(telegram)) self.assertEqual(climate.is_on, True) climate_inv = Climate( xknx, "TestClimate", group_address_on_off="1/2/2", on_off_invert=True ) - telegram = Telegram(destination_address=GroupAddress("1/2/2")) - telegram.payload = DPTBinary(1) + telegram = Telegram( + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(1)), + ) self.loop.run_until_complete(climate_inv.process(telegram)) self.assertEqual(climate_inv.is_on, False) @@ -1373,7 +1416,8 @@ def test_power_on_off(self): self.assertEqual( _telegram, Telegram( - destination_address=GroupAddress("1/2/2"), payload=DPTBinary(True) + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(True)), ), ) self.loop.run_until_complete(climate.turn_off()) @@ -1383,7 +1427,8 @@ def test_power_on_off(self): self.assertEqual( _telegram, Telegram( - destination_address=GroupAddress("1/2/2"), payload=DPTBinary(False) + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(False)), ), ) @@ -1397,7 +1442,8 @@ def test_power_on_off(self): self.assertEqual( _telegram, Telegram( - destination_address=GroupAddress("1/2/2"), payload=DPTBinary(False) + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(False)), ), ) self.loop.run_until_complete(climate_inv.turn_off()) @@ -1407,6 +1453,7 @@ def test_power_on_off(self): self.assertEqual( _telegram, Telegram( - destination_address=GroupAddress("1/2/2"), payload=DPTBinary(True) + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(True)), ), ) diff --git a/test/devices_tests/cover_test.py b/test/devices_tests/cover_test.py index c70353379..cce88dccd 100644 --- a/test/devices_tests/cover_test.py +++ b/test/devices_tests/cover_test.py @@ -7,7 +7,8 @@ from xknx import XKNX from xknx.devices import Cover from xknx.dpt import DPTArray, DPTBinary -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueWrite class TestCover(unittest.TestCase): @@ -129,8 +130,7 @@ def test_sync(self): self.assertEqual( telegram1, Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ), ) @@ -151,8 +151,7 @@ def test_sync_state(self): self.assertEqual( telegram1, Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), ) @@ -173,16 +172,14 @@ def test_sync_angle(self): self.assertEqual( telegram1, Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ), ) telegram2 = xknx.telegrams.get_nowait() self.assertEqual( telegram2, Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), ) @@ -203,8 +200,7 @@ def test_sync_angle_state(self): self.assertEqual( telegram1, Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), ) @@ -228,7 +224,10 @@ def test_set_up(self): # DPT 1.008 - 0:up 1:down self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) # @@ -250,7 +249,10 @@ def test_set_short_down(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # @@ -273,7 +275,10 @@ def test_set_down_inverted(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) # @@ -296,7 +301,10 @@ def test_set_short_up(self): # DPT 1.008 - 0:up 1:down self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) # @@ -320,7 +328,10 @@ def test_set_up_inverted(self): # DPT 1.008 - 0:up 1:down self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # @@ -343,7 +354,10 @@ def test_set_down(self): # DPT 1.008 - 0:up 1:down self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # @@ -365,7 +379,10 @@ def test_stop(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/2"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) cover_manual_stop = Cover( @@ -382,7 +399,10 @@ def test_stop(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/0"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/0"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # @@ -404,7 +424,10 @@ def test_position(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(0x80)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(0x80)), + ), ) def test_position_without_position_address_up(self): @@ -424,7 +447,10 @@ def test_position_without_position_address_up(self): # DPT 1.008 - 0:up 1:down self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) self.assertEqual(cover.travelcalculator.travel_to_position, 50) self.assertTrue(cover.is_opening()) @@ -449,7 +475,10 @@ def test_position_without_position_address_down(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) self.assertEqual(cover.travelcalculator.travel_to_position, 80) self.assertTrue(cover.is_closing()) @@ -480,7 +509,10 @@ def test_position_without_position_address_uninitialized_up(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) def test_position_without_position_address_uninitialized_down(self): @@ -506,7 +538,10 @@ def test_position_without_position_address_uninitialized_down(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) def test_angle(self): @@ -528,7 +563,8 @@ def test_angle(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/4/18"), payload=DPTArray(0x80) + destination_address=GroupAddress("1/4/18"), + payload=GroupValueWrite(DPTArray(0x80)), ), ) @@ -564,14 +600,14 @@ def test_process_position(self): ) # initial position process - position is unknown so this is the new state - not moving telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray(213) + GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(213)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertEqual(cover.current_position(), 84) self.assertFalse(cover.is_traveling()) # state telegram updates current position - we are not moving so this is new state - not moving telegram = Telegram( - destination_address=GroupAddress("1/2/4"), payload=DPTArray(42) + GroupAddress("1/2/4"), payload=GroupValueWrite(DPTArray(42)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertEqual(cover.current_position(), 16) @@ -579,7 +615,7 @@ def test_process_position(self): self.assertEqual(cover.travelcalculator.travel_to_position, 16) # new position - movement starts telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray(255) + GroupAddress("1/2/3"), payload=GroupValueWrite(DPTArray(255)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertEqual(cover.current_position(), 16) @@ -587,7 +623,7 @@ def test_process_position(self): self.assertEqual(cover.travelcalculator.travel_to_position, 100) # new state while moving - movement goes on; travelcalculator updated telegram = Telegram( - destination_address=GroupAddress("1/2/4"), payload=DPTArray(213) + GroupAddress("1/2/4"), payload=GroupValueWrite(DPTArray(213)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertEqual(cover.current_position(), 84) @@ -606,7 +642,7 @@ def test_process_angle(self): group_address_angle_state="1/2/4", ) telegram = Telegram( - destination_address=GroupAddress("1/2/4"), payload=DPTArray(42) + GroupAddress("1/2/4"), payload=GroupValueWrite(DPTArray(42)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertEqual(cover.current_angle(), 16) @@ -620,7 +656,7 @@ def test_process_up(self): cover.travelcalculator.set_position(50) self.assertFalse(cover.is_traveling()) telegram = Telegram( - destination_address=GroupAddress("1/2/1"), payload=DPTBinary(0) + GroupAddress("1/2/1"), payload=GroupValueWrite(DPTBinary(0)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertTrue(cover.is_opening()) @@ -634,7 +670,7 @@ def test_process_down(self): cover.travelcalculator.set_position(50) self.assertFalse(cover.is_traveling()) telegram = Telegram( - destination_address=GroupAddress("1/2/1"), payload=DPTBinary(1) + GroupAddress("1/2/1"), payload=GroupValueWrite(DPTBinary(1)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertTrue(cover.is_closing()) @@ -652,7 +688,7 @@ def test_process_stop(self): self.loop.run_until_complete(cover.set_down()) self.assertTrue(cover.is_traveling()) telegram = Telegram( - destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1) + GroupAddress("1/2/2"), payload=GroupValueWrite(DPTBinary(1)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertFalse(cover.is_traveling()) @@ -670,7 +706,7 @@ def test_process_short_stop(self): self.loop.run_until_complete(cover.set_down()) self.assertTrue(cover.is_traveling()) telegram = Telegram( - destination_address=GroupAddress("1/2/2"), payload=DPTBinary(1) + GroupAddress("1/2/2"), payload=GroupValueWrite(DPTBinary(1)) ) self.loop.run_until_complete(cover.process(telegram)) self.assertFalse(cover.is_traveling()) @@ -708,14 +744,15 @@ async def async_after_update_callback(device): ]: with self.subTest(address=address, feature=feature): telegram = Telegram( - destination_address=GroupAddress(address), payload=payload + destination_address=GroupAddress(address), + payload=GroupValueWrite(payload), ) self.loop.run_until_complete(cover.process(telegram)) after_update_callback.assert_called_with(cover) after_update_callback.reset_mock() # Stop only when cover is travelling telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1)) ) self.loop.run_until_complete(cover.process(telegram)) after_update_callback.assert_not_called() diff --git a/test/devices_tests/datetime_test.py b/test/devices_tests/datetime_test.py index c47cbece6..ecd9aae50 100644 --- a/test/devices_tests/datetime_test.py +++ b/test/devices_tests/datetime_test.py @@ -7,7 +7,8 @@ from xknx import XKNX from xknx.devices import DateTime from xknx.dpt import DPTArray -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class TestDateTime(unittest.TestCase): @@ -43,10 +44,10 @@ def test_sync_datetime(self): self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram.destination_address, GroupAddress("1/2/3")) - self.assertEqual(telegram.telegramtype, TelegramType.GROUP_WRITE) - self.assertEqual(len(telegram.payload.value), 8) + self.assertEqual(len(telegram.payload.value.value), 8) self.assertEqual( - telegram.payload.value, (0x75, 0x01, 0x07, 0xE9, 0x0D, 0x0E, 0x20, 0x80) + telegram.payload.value.value, + (0x75, 0x01, 0x07, 0xE9, 0x0D, 0x0E, 0x20, 0x80), ) # @@ -69,9 +70,8 @@ def test_sync_date(self): telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram.destination_address, GroupAddress("1/2/3")) - self.assertEqual(telegram.telegramtype, TelegramType.GROUP_WRITE) - self.assertEqual(len(telegram.payload.value), 3) - self.assertEqual(telegram.payload.value, (0x07, 0x01, 0x11)) + self.assertEqual(len(telegram.payload.value.value), 3) + self.assertEqual(telegram.payload.value.value, (0x07, 0x01, 0x11)) # # SYNC Time @@ -93,9 +93,8 @@ def test_sync_time(self): telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram.destination_address, GroupAddress("1/2/3")) - self.assertEqual(telegram.telegramtype, TelegramType.GROUP_WRITE) - self.assertEqual(len(telegram.payload.value), 3) - self.assertEqual(telegram.payload.value, (0xE9, 0x0D, 0x0E)) + self.assertEqual(len(telegram.payload.value.value), 3) + self.assertEqual(telegram.payload.value.value, (0xE9, 0x0D, 0x0E)) # # PROCESS @@ -111,8 +110,7 @@ def test_process_read(self): ) telegram_read = Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ) with patch("time.localtime") as mock_time: mock_time.return_value = time.struct_time([2017, 1, 7, 9, 13, 14, 6, 0, 0]) @@ -127,8 +125,7 @@ def test_process_read(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_RESPONSE, - payload=DPTArray((0xE9, 0xD, 0xE)), + payload=GroupValueResponse(DPTArray((0xE9, 0xD, 0xE))), ), ) diff --git a/test/devices_tests/device_test.py b/test/devices_tests/device_test.py index c925e0b43..ea607f58c 100644 --- a/test/devices_tests/device_test.py +++ b/test/devices_tests/device_test.py @@ -6,7 +6,8 @@ from xknx import XKNX from xknx.devices import Device, Sensor from xknx.dpt import DPTArray -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class TestDevice(unittest.TestCase): @@ -102,9 +103,7 @@ def test_process(self): fut.set_result(None) mock_group_read.return_value = fut telegram = Telegram( - destination_address=GroupAddress("1/2/1"), - payload=DPTArray((0x01, 0x02)), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/1"), payload=GroupValueRead() ) self.loop.run_until_complete(device.process(telegram)) mock_group_read.assert_called_with(telegram) @@ -115,8 +114,7 @@ def test_process(self): mock_group_write.return_value = fut telegram = Telegram( destination_address=GroupAddress("1/2/1"), - payload=DPTArray((0x01, 0x02)), - telegramtype=TelegramType.GROUP_WRITE, + payload=GroupValueWrite(DPTArray((0x01, 0x02))), ) self.loop.run_until_complete(device.process(telegram)) mock_group_write.assert_called_with(telegram) @@ -127,8 +125,7 @@ def test_process(self): mock_group_response.return_value = fut telegram = Telegram( destination_address=GroupAddress("1/2/1"), - payload=DPTArray((0x01, 0x02)), - telegramtype=TelegramType.GROUP_RESPONSE, + payload=GroupValueResponse(DPTArray((0x01, 0x02))), ) self.loop.run_until_complete(device.process(telegram)) mock_group_response.assert_called_with(telegram) diff --git a/test/devices_tests/expose_sensor_test.py b/test/devices_tests/expose_sensor_test.py index c7fbafe17..86cbe57fd 100644 --- a/test/devices_tests/expose_sensor_test.py +++ b/test/devices_tests/expose_sensor_test.py @@ -6,7 +6,8 @@ from xknx import XKNX from xknx.devices import ExposeSensor from xknx.dpt import DPTArray, DPTBinary -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class TestExposeSensor(unittest.TestCase): @@ -73,8 +74,7 @@ def test_set_binary(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_WRITE, - payload=DPTBinary(0), + payload=GroupValueWrite(DPTBinary(0)), ), ) @@ -92,8 +92,7 @@ def test_set_percent(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_WRITE, - payload=DPTArray((0xBF,)), + payload=GroupValueWrite(DPTArray((0xBF,))), ), ) @@ -110,8 +109,7 @@ def test_set_temperature(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_WRITE, - payload=DPTArray((0x0C, 0x1A)), + payload=GroupValueWrite(DPTArray((0x0C, 0x1A))), ), ) @@ -126,8 +124,7 @@ def test_process_binary(self): ) expose_sensor.sensor_value.payload = DPTBinary(1) - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.telegramtype = TelegramType.GROUP_READ + telegram = Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()) self.loop.run_until_complete(expose_sensor.process(telegram)) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() @@ -135,8 +132,7 @@ def test_process_binary(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_RESPONSE, - payload=DPTBinary(True), + payload=GroupValueResponse(DPTBinary(True)), ), ) @@ -148,8 +144,7 @@ def test_process_percent(self): ) expose_sensor.sensor_value.payload = DPTArray((0x40,)) - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.telegramtype = TelegramType.GROUP_READ + telegram = Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()) self.loop.run_until_complete(expose_sensor.process(telegram)) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() @@ -157,8 +152,7 @@ def test_process_percent(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_RESPONSE, - payload=DPTArray((0x40,)), + payload=GroupValueResponse(DPTArray((0x40,))), ), ) @@ -170,8 +164,7 @@ def test_process_temperature(self): ) expose_sensor.sensor_value.payload = DPTArray((0x0C, 0x1A)) - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.telegramtype = TelegramType.GROUP_READ + telegram = Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()) self.loop.run_until_complete(expose_sensor.process(telegram)) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() @@ -179,8 +172,7 @@ def test_process_temperature(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_RESPONSE, - payload=DPTArray((0x0C, 0x1A)), + payload=GroupValueResponse(DPTArray((0x0C, 0x1A))), ), ) diff --git a/test/devices_tests/fan_test.py b/test/devices_tests/fan_test.py index f9715e6ba..0e7a7767f 100644 --- a/test/devices_tests/fan_test.py +++ b/test/devices_tests/fan_test.py @@ -7,7 +7,8 @@ from xknx.devices import Fan from xknx.dpt import DPTArray, DPTBinary from xknx.exceptions import CouldNotParseTelegram -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueWrite class TestFan(unittest.TestCase): @@ -39,8 +40,7 @@ def test_sync(self): self.assertEqual( telegram1, Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ), ) @@ -64,8 +64,7 @@ def test_sync_state_address(self): self.assertEqual( telegram1, Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), ) @@ -83,7 +82,10 @@ def test_set_speed(self): # 140 is 55% as byte (0...255) self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTArray(140)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(140)), + ), ) # @@ -97,7 +99,8 @@ def test_process_speed(self): # 140 is 55% as byte (0...255) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray(140) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(140)), ) self.loop.run_until_complete(fan.process(telegram)) self.assertEqual(fan.current_speed, 55) @@ -107,7 +110,8 @@ def test_process_speed_wrong_payload(self): # pylint: disable=invalid-name xknx = XKNX() fan = Fan(xknx, name="TestFan", group_address_speed="1/2/3") telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(fan.process(telegram)) @@ -118,7 +122,8 @@ def test_process_fan_payload_invalid_length(self): xknx = XKNX() fan = Fan(xknx, name="TestFan", group_address_speed="1/2/3") telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((23, 24)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((23, 24))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(fan.process(telegram)) diff --git a/test/devices_tests/light_test.py b/test/devices_tests/light_test.py index 0d6e8b104..2e54fb9d5 100644 --- a/test/devices_tests/light_test.py +++ b/test/devices_tests/light_test.py @@ -7,7 +7,8 @@ from xknx.devices import Light from xknx.dpt import DPTArray, DPTBinary from xknx.exceptions import CouldNotParseTelegram -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueWrite class TestLight(unittest.TestCase): @@ -156,33 +157,27 @@ def test_sync(self): test_telegrams = [ Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/5"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/5"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/6"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/6"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/7"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/9"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/8"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/7"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/9"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/8"), payload=GroupValueRead() ), ] - self.assertEqual(len(set(telegrams)), 6) - self.assertEqual(set(telegrams), set(test_telegrams)) + self.assertEqual(len(telegrams), 6) + self.assertListEqual(telegrams, test_telegrams) # # SYNC WITH STATE ADDRESS @@ -216,33 +211,27 @@ def test_sync_state_address(self): test_telegrams = [ Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/6"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/6"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/8"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/8"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/10"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/14"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/12"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/10"), payload=GroupValueRead() ), Telegram( - destination_address=GroupAddress("1/2/14"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/12"), payload=GroupValueRead() ), ] - self.assertEqual(len(set(telegrams)), 6) - self.assertEqual(set(telegrams), set(test_telegrams)) + self.assertEqual(len(telegrams), 6) + self.assertListEqual(telegrams, test_telegrams) # # TEST SET ON @@ -261,7 +250,10 @@ def test_set_on(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # @@ -281,7 +273,10 @@ def test_set_off(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) # @@ -301,7 +296,10 @@ def test_set_brightness(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)), + Telegram( + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray(23)), + ), ) def test_set_brightness_not_dimmable(self): @@ -335,7 +333,7 @@ def test_set_color(self): telegram, Telegram( destination_address=GroupAddress("1/2/5"), - payload=DPTArray((23, 24, 25)), + payload=GroupValueWrite(DPTArray((23, 24, 25))), ), ) self.loop.run_until_complete(xknx.devices.process(telegram)) @@ -373,7 +371,7 @@ def test_set_color_rgbw(self): telegram, Telegram( destination_address=GroupAddress("1/2/5"), - payload=DPTArray((23, 24, 25, 26, 0, 15)), + payload=GroupValueWrite(DPTArray((23, 24, 25, 26, 0, 15))), ), ) self.loop.run_until_complete(xknx.devices.process(telegram)) @@ -414,7 +412,10 @@ def test_set_tw(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/5"), payload=DPTArray(23)), + Telegram( + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray(23)), + ), ) def test_set_tw_unsupported(self): @@ -448,10 +449,12 @@ def test_set_color_temp(self): telegram, Telegram( destination_address=GroupAddress("1/2/5"), - payload=DPTArray( - ( - 0x0F, - 0xA0, + payload=GroupValueWrite( + DPTArray( + ( + 0x0F, + 0xA0, + ) ) ), ), @@ -484,13 +487,15 @@ def test_process_switch(self): self.assertEqual(light.state, None) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(light.process(telegram)) self.assertEqual(light.state, True) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.loop.run_until_complete(light.process(telegram)) self.assertEqual(light.state, False) @@ -515,7 +520,8 @@ async def async_after_update_callback(device): light.register_device_updated_cb(async_after_update_callback) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(light.process(telegram)) @@ -533,7 +539,8 @@ def test_process_dimm(self): self.assertEqual(light.current_brightness, None) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray(23) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray(23)), ) self.loop.run_until_complete(light.process(telegram)) self.assertEqual(light.current_brightness, 23) @@ -548,7 +555,8 @@ def test_process_dimm_wrong_payload(self): group_address_brightness="1/2/5", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(light.process(telegram)) @@ -564,7 +572,8 @@ def test_process_dimm_payload_invalid_length(self): group_address_brightness="1/2/5", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24)) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray((23, 24))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(light.process(telegram)) @@ -580,7 +589,8 @@ def test_process_color(self): ) self.assertEqual(light.current_color, (None, None)) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24, 25)) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray((23, 24, 25))), ) self.loop.run_until_complete(light.process(telegram)) self.assertEqual(light.current_color, ((23, 24, 25), None)) @@ -598,7 +608,7 @@ def test_process_color_rgbw(self): self.assertEqual(light.current_color, (None, None)) telegram = Telegram( destination_address=GroupAddress("1/2/5"), - payload=DPTArray((23, 24, 25, 26, 0, 15)), + payload=GroupValueWrite(DPTArray((23, 24, 25, 26, 0, 15))), ) self.loop.run_until_complete(light.process(telegram)) self.assertEqual(light.current_color, ([23, 24, 25], 26)) @@ -615,7 +625,8 @@ def test_process_tunable_white(self): self.assertEqual(light.current_tunable_white, None) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray(23) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray(23)), ) self.loop.run_until_complete(light.process(telegram)) self.assertEqual(light.current_tunable_white, 23) @@ -630,7 +641,8 @@ def test_process_tunable_white_wrong_payload(self): group_address_tunable_white="1/2/5", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(light.process(telegram)) @@ -646,7 +658,8 @@ def test_process_tunable_white_payload_invalid_length(self): group_address_tunable_white="1/2/5", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray((23, 24)) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray((23, 24))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(light.process(telegram)) @@ -664,10 +677,12 @@ def test_process_color_temperature(self): telegram = Telegram( destination_address=GroupAddress("1/2/5"), - payload=DPTArray( - ( - 0x0F, - 0xA0, + payload=GroupValueWrite( + DPTArray( + ( + 0x0F, + 0xA0, + ) ) ), ) @@ -684,7 +699,8 @@ def test_process_color_temperature_wrong_payload(self): group_address_color_temperature="1/2/5", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(light.process(telegram)) @@ -700,7 +716,8 @@ def test_process_color_temperature_payload_invalid_length(self): group_address_color_temperature="1/2/5", ) telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTArray(23) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTArray(23)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(light.process(telegram)) diff --git a/test/devices_tests/notification_test.py b/test/devices_tests/notification_test.py index 31e5a22c0..c5a1bf4e0 100644 --- a/test/devices_tests/notification_test.py +++ b/test/devices_tests/notification_test.py @@ -7,7 +7,8 @@ from xknx.devices import Notification from xknx.dpt import DPTArray, DPTBinary, DPTString from xknx.exceptions import CouldNotParseTelegram -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueWrite class TestNotification(unittest.TestCase): @@ -37,8 +38,7 @@ def test_sync_state(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), ) @@ -51,14 +51,14 @@ def test_process(self): notification = Notification(xknx, "Warning", group_address="1/2/3") telegram_set = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray(DPTString().to_knx("Ein Prosit!")), + payload=GroupValueWrite(DPTArray(DPTString().to_knx("Ein Prosit!"))), ) self.loop.run_until_complete(notification.process(telegram_set)) self.assertEqual(notification.message, "Ein Prosit!") telegram_unset = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray(DPTString().to_knx("")), + payload=GroupValueWrite(DPTArray(DPTString().to_knx(""))), ) self.loop.run_until_complete(notification.process(telegram_unset)) self.assertEqual(notification.message, "") @@ -77,7 +77,7 @@ async def async_after_update_callback(device): notification.register_device_updated_cb(async_after_update_callback) telegram_set = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray(DPTString().to_knx("Ein Prosit!")), + payload=GroupValueWrite(DPTArray(DPTString().to_knx("Ein Prosit!"))), ) self.loop.run_until_complete(notification.process(telegram_set)) after_update_callback.assert_called_with(notification) @@ -88,7 +88,8 @@ def test_process_payload_invalid_length(self): xknx = XKNX() notification = Notification(xknx, "Warning", group_address="1/2/3") telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((23, 24)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((23, 24))), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(notification.process(telegram)) @@ -98,7 +99,8 @@ def test_process_wrong_payload(self): xknx = XKNX() notification = Notification(xknx, "Warning", group_address="1/2/3") telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(notification.process(telegram)) @@ -117,7 +119,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray(DPTString().to_knx("Ein Prosit!")), + payload=GroupValueWrite(DPTArray(DPTString().to_knx("Ein Prosit!"))), ), ) # test if message longer than 14 chars gets cropped @@ -129,7 +131,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray(DPTString().to_knx("This is too lo")), + payload=GroupValueWrite(DPTArray(DPTString().to_knx("This is too lo"))), ), ) diff --git a/test/devices_tests/scene_test.py b/test/devices_tests/scene_test.py index 671b94003..9898bfc69 100644 --- a/test/devices_tests/scene_test.py +++ b/test/devices_tests/scene_test.py @@ -8,6 +8,7 @@ from xknx.devices import Scene from xknx.dpt import DPTArray from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestScene(unittest.TestCase): @@ -46,7 +47,10 @@ def test_run(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTArray(0x16)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTArray(0x16)), + ), ) def test_do(self): @@ -58,7 +62,10 @@ def test_do(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/1"), payload=DPTArray(0x16)), + Telegram( + destination_address=GroupAddress("1/2/1"), + payload=GroupValueWrite(DPTArray(0x16)), + ), ) def test_wrong_do(self): diff --git a/test/devices_tests/sensor_expose_loop_test.py b/test/devices_tests/sensor_expose_loop_test.py index db73175b9..e910a390d 100644 --- a/test/devices_tests/sensor_expose_loop_test.py +++ b/test/devices_tests/sensor_expose_loop_test.py @@ -5,7 +5,8 @@ from xknx import XKNX from xknx.devices import BinarySensor, ExposeSensor, Sensor from xknx.dpt import DPTArray, DPTBinary -from xknx.telegram import GroupAddress, Telegram, TelegramDirection, TelegramType +from xknx.telegram import GroupAddress, Telegram, TelegramDirection +from xknx.telegram.apci import GroupValueWrite class SensorExposeLoopTest(unittest.TestCase): @@ -1460,9 +1461,8 @@ def test_array_sensor_loop(self): incoming_telegram = Telegram( destination_address=GroupAddress("1/1/1"), - telegramtype=TelegramType.GROUP_WRITE, direction=TelegramDirection.INCOMING, - payload=test_payload, + payload=GroupValueWrite(test_payload), ) self.loop.run_until_complete(sensor.process(incoming_telegram)) @@ -1482,9 +1482,8 @@ def test_array_sensor_loop(self): outgoing_telegram, Telegram( destination_address=GroupAddress("2/2/2"), - telegramtype=TelegramType.GROUP_WRITE, direction=TelegramDirection.OUTGOING, - payload=test_payload, + payload=GroupValueWrite(test_payload), ), ) @@ -1510,9 +1509,8 @@ def test_binary_sensor_loop(self): incoming_telegram = Telegram( destination_address=GroupAddress("1/1/1"), - telegramtype=TelegramType.GROUP_WRITE, direction=TelegramDirection.INCOMING, - payload=test_payload, + payload=GroupValueWrite(test_payload), ) self.loop.run_until_complete(sensor.process(incoming_telegram)) @@ -1526,8 +1524,7 @@ def test_binary_sensor_loop(self): outgoing_telegram, Telegram( destination_address=GroupAddress("2/2/2"), - telegramtype=TelegramType.GROUP_WRITE, direction=TelegramDirection.OUTGOING, - payload=test_payload, + payload=GroupValueWrite(test_payload), ), ) diff --git a/test/devices_tests/sensor_test.py b/test/devices_tests/sensor_test.py index 7157ce386..ad928f3cf 100644 --- a/test/devices_tests/sensor_test.py +++ b/test/devices_tests/sensor_test.py @@ -6,7 +6,8 @@ from xknx import XKNX from xknx.devices import Sensor from xknx.dpt import DPTArray -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class TestSensor(unittest.TestCase): @@ -78,11 +79,12 @@ async def async_after_update_callback(device): # set initial payload of sensor sensor.sensor_value.payload = payload - telegram = Telegram(destination_address=GroupAddress("1/2/3"), payload=payload) + telegram = Telegram( + destination_address=GroupAddress("1/2/3"), payload=GroupValueWrite(payload) + ) response_telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=payload, - telegramtype=TelegramType.GROUP_RESPONSE, + payload=GroupValueResponse(payload), ) # verify not called when always_callback is False @@ -2642,8 +2644,7 @@ def test_sync(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ), ) @@ -2669,8 +2670,10 @@ def test_process(self): xknx, "TestSensor", value_type="temperature", group_address_state="1/2/3" ) - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.payload = DPTArray((0x06, 0xA0)) + telegram = Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x06, 0xA0))), + ) self.loop.run_until_complete(sensor.process(telegram)) self.assertEqual(sensor.sensor_value.payload, DPTArray((0x06, 0xA0))) self.assertEqual(sensor.resolve_state(), 16.96) @@ -2691,7 +2694,9 @@ async def async_after_update_callback(device): sensor.register_device_updated_cb(async_after_update_callback) - telegram = Telegram(destination_address=GroupAddress("1/2/3")) - telegram.payload = DPTArray((0x01, 0x02)) + telegram = Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x01, 0x02))), + ) self.loop.run_until_complete(sensor.process(telegram)) after_update_callback.assert_called_with(sensor) diff --git a/test/devices_tests/switch_test.py b/test/devices_tests/switch_test.py index 2ed54db4d..a26e0c233 100644 --- a/test/devices_tests/switch_test.py +++ b/test/devices_tests/switch_test.py @@ -6,7 +6,8 @@ from xknx import XKNX from xknx.devices import Switch from xknx.dpt import DPTBinary -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class AsyncMock(MagicMock): @@ -44,8 +45,7 @@ def test_sync(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/3"), payload=GroupValueRead() ), ) @@ -63,8 +63,7 @@ def test_sync_state_address(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/4"), - telegramtype=TelegramType.GROUP_READ, + destination_address=GroupAddress("1/2/4"), payload=GroupValueRead() ), ) @@ -87,10 +86,12 @@ def test_process(self): callback_mock.assert_not_called() telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) telegram_off = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.loop.run_until_complete(switch1.process(telegram_on)) @@ -136,13 +137,11 @@ def test_process_state(self): telegram_on = Telegram( destination_address=GroupAddress("1/2/4"), - payload=DPTBinary(1), - telegramtype=TelegramType.GROUP_RESPONSE, + payload=GroupValueResponse(DPTBinary(1)), ) telegram_off = Telegram( destination_address=GroupAddress("1/2/4"), - payload=DPTBinary(0), - telegramtype=TelegramType.GROUP_RESPONSE, + payload=GroupValueResponse(DPTBinary(0)), ) self.loop.run_until_complete(switch1.process(telegram_on)) @@ -170,10 +169,12 @@ def test_process_invert(self): self.assertEqual(switch.state, None) telegram_inv_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) telegram_inv_off = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(switch.process(telegram_inv_on)) @@ -189,7 +190,8 @@ def test_process_reset_after(self): xknx, "TestInput", group_address="1/2/3", reset_after=reset_after_sec ) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(switch.process(telegram_on)) @@ -208,7 +210,8 @@ def test_process_reset_after_cancel_existing(self): xknx, "TestInput", group_address="1/2/3", reset_after=reset_after_sec ) telegram_on = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueResponse(DPTBinary(1)), ) self.loop.run_until_complete(switch.process(telegram_on)) @@ -238,7 +241,8 @@ async def async_after_update_callback(device): switch.register_device_updated_cb(async_after_update_callback) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(switch.process(telegram)) @@ -256,7 +260,10 @@ def test_set_on(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # @@ -271,7 +278,10 @@ def test_set_off(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) # @@ -287,7 +297,10 @@ def test_set_invert(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) self.loop.run_until_complete(switch.set_off()) @@ -295,7 +308,10 @@ def test_set_invert(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) # diff --git a/test/io_tests/tunnelling_test.py b/test/io_tests/tunnelling_test.py index 198fe70e4..47ffd17dc 100644 --- a/test/io_tests/tunnelling_test.py +++ b/test/io_tests/tunnelling_test.py @@ -8,6 +8,7 @@ from xknx.io import Tunnelling, UDPClient from xknx.knxip import ErrorCode, KNXIPFrame, KNXIPServiceType, TunnellingAck from xknx.telegram import GroupAddress, IndividualAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestTunnelling(unittest.TestCase): @@ -29,7 +30,8 @@ def test_tunnelling(self): communication_channel_id = 23 udp_client = UDPClient(xknx, ("192.168.1.1", 0), ("192.168.1.2", 1234)) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x1, 0x2, 0x3)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x1, 0x2, 0x3))), ) sequence_counter = 42 src_address = IndividualAddress("2.2.2") diff --git a/test/knxip_tests/cemi_frame_test.py b/test/knxip_tests/cemi_frame_test.py index c3804a2ca..84993d928 100644 --- a/test/knxip_tests/cemi_frame_test.py +++ b/test/knxip_tests/cemi_frame_test.py @@ -6,8 +6,9 @@ from xknx.dpt import DPTBinary, DPTComparator from xknx.exceptions import ConversionError, CouldNotParseKNXIP, UnsupportedCEMIMessage from xknx.knxip.cemi_frame import CEMIFrame -from xknx.knxip.knxip_enum import APCICommand, CEMIFlags, CEMIMessageCode +from xknx.knxip.knxip_enum import CEMIFlags, CEMIMessageCode from xknx.telegram import GroupAddress, IndividualAddress, Telegram +from xknx.telegram.apci import GroupValueRead def get_data(code, adil, flags, src, dst, mpdu_len, tpci_apci, payload): @@ -40,10 +41,9 @@ def test_valid_command(frame): """Test for valid frame parsing""" packet_len = frame.from_knx(get_data(0x29, 0, 0, 0, 0, 1, 0, [])) assert frame.code == CEMIMessageCode.L_DATA_IND - assert frame.cmd == APCICommand.GROUP_READ assert frame.flags == 0 assert frame.mpdu_len == 1 - assert frame.payload == DPTBinary(0) + assert frame.payload == GroupValueRead() assert frame.src_addr == IndividualAddress(0) assert frame.dst_addr == IndividualAddress(0) assert packet_len == 11 @@ -64,10 +64,9 @@ def test_invalid_apdu_len(frame): def test_invalid_src_addr(frame): """Test for invalid src addr""" frame.code = CEMIMessageCode.L_DATA_IND - frame.cmd = APCICommand.GROUP_READ frame.flags = 0 frame.mpdu_len = 1 - frame.payload = DPTBinary(0) + frame.payload = GroupValueRead() frame.src_addr = None frame.dst_addr = IndividualAddress(0) @@ -78,10 +77,9 @@ def test_invalid_src_addr(frame): def test_invalid_dst_addr(frame): """Test for invalid dst addr""" frame.code = CEMIMessageCode.L_DATA_IND - frame.cmd = APCICommand.GROUP_READ frame.flags = 0 frame.mpdu_len = 1 - frame.payload = DPTBinary(0) + frame.payload = GroupValueRead() frame.src_addr = IndividualAddress(0) frame.dst_addr = None @@ -89,24 +87,9 @@ def test_invalid_dst_addr(frame): frame.to_knx() -def test_no_payload(frame): - """Test for having no payload set""" - frame.code = CEMIMessageCode.L_DATA_IND - frame.cmd = APCICommand.GROUP_READ - frame.flags = 0 - frame.mpdu_len = 1 - frame.payload = None - frame.src_addr = IndividualAddress(0) - frame.dst_addr = IndividualAddress(0) - - assert [41, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0] == frame.to_knx() - assert 11 == frame.calculated_length() - - def test_invalid_payload(frame): """Test for having wrong payload set""" frame.code = CEMIMessageCode.L_DATA_IND - frame.cmd = APCICommand.GROUP_READ frame.flags = 0 frame.mpdu_len = 1 frame.payload = DPTComparator() @@ -134,21 +117,6 @@ def test_from_knx_with_not_implemented_cemi(frame): ) -def test_invalid_telegram(frame): - """Test for invalid telegram type""" - packet_len = frame.from_knx(get_data(0x29, 0, 0, 0, 0, 1, 0, [])) - frame.cmd = None - with raises(ConversionError, match=r".*Telegram not implemented for.*"): - tg = frame.telegram - - -def test_set_invalid_telegram(frame): - """Test for setting invalid telegram type""" - tg = Telegram(telegramtype=None) - with raises(TypeError): - frame.telegram = tg - - def test_invalid_invalid_len(frame): """Test for invalid cemi len""" with raises(UnsupportedCEMIMessage, match=r".*CEMI too small.*"): diff --git a/test/knxip_tests/routing_indication_test.py b/test/knxip_tests/routing_indication_test.py index fed7dda78..4a2dc6f03 100644 --- a/test/knxip_tests/routing_indication_test.py +++ b/test/knxip_tests/routing_indication_test.py @@ -6,7 +6,8 @@ from xknx import XKNX from xknx.dpt import DPTArray, DPTBinary, DPTTemperature, DPTTime from xknx.knxip import CEMIFrame, KNXIPFrame, KNXIPServiceType, RoutingIndication -from xknx.telegram import GroupAddress, IndividualAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, IndividualAddress, Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite class Test_KNXIP(unittest.TestCase): @@ -36,8 +37,8 @@ def test_from_knx(self): self.assertEqual(knxipframe.body.cemi.src_addr, IndividualAddress("1.2.2")) self.assertEqual(knxipframe.body.cemi.dst_addr, GroupAddress(337)) - self.assertEqual(len(knxipframe.body.cemi.payload.value), 1) - self.assertEqual(knxipframe.body.cemi.payload.value[0], 0xF0) + self.assertEqual(len(knxipframe.body.cemi.payload.value.value), 1) + self.assertEqual(knxipframe.body.cemi.payload.value.value[0], 0xF0) def test_from_knx_to_knx(self): """Test parsing and streaming CEMIFrame KNX/IP.""" @@ -61,7 +62,9 @@ def test_telegram_set(self): telegram = Telegram( destination_address=GroupAddress(337), - payload=DPTArray(DPTTime().to_knx(time.strptime("13:23:42", "%H:%M:%S"))), + payload=GroupValueWrite( + DPTArray(DPTTime().to_knx(time.strptime("13:23:42", "%H:%M:%S"))) + ), ) knxipframe.body.cemi.telegram = telegram @@ -86,8 +89,8 @@ def test_telegram_get(self): self.assertEqual(telegram.destination_address, GroupAddress(337)) - self.assertEqual(len(telegram.payload.value), 1) - self.assertEqual(telegram.payload.value[0], 0xF0) + self.assertEqual(len(telegram.payload.value.value), 1) + self.assertEqual(telegram.payload.value.value[0], 0xF0) # # End-tox-End tests: @@ -109,7 +112,7 @@ def test_EndTOEnd_group_write_binary_on(self): telegram, Telegram( destination_address=GroupAddress("329"), - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), source_address=IndividualAddress("15.15.249"), ), ) @@ -137,7 +140,7 @@ def test_EndTOEnd_group_write_binary_off(self): telegram, Telegram( destination_address=GroupAddress("329"), - payload=DPTBinary(0), + payload=GroupValueWrite(DPTBinary(0)), source_address=IndividualAddress("15.15.249"), ), ) @@ -165,7 +168,7 @@ def test_EndTOEnd_group_write_1byte(self): telegram, Telegram( destination_address=GroupAddress("331"), - payload=DPTArray(0x65), + payload=GroupValueWrite(DPTArray(0x65)), source_address=IndividualAddress("15.15.249"), ), ) @@ -193,7 +196,7 @@ def test_EndTOEnd_group_write_2bytes(self): telegram, Telegram( destination_address=GroupAddress("2049"), - payload=DPTArray(DPTTemperature().to_knx(19.85)), + payload=GroupValueWrite(DPTArray(DPTTemperature().to_knx(19.85))), source_address=IndividualAddress("1.4.2"), ), ) @@ -221,7 +224,7 @@ def test_EndTOEnd_group_read(self): telegram, Telegram( destination_address=GroupAddress("440"), - telegramtype=TelegramType.GROUP_READ, + payload=GroupValueRead(), source_address=IndividualAddress("15.15.249"), ), ) @@ -249,8 +252,7 @@ def test_EndTOEnd_group_response(self): telegram, Telegram( destination_address=GroupAddress("392"), - telegramtype=TelegramType.GROUP_RESPONSE, - payload=DPTBinary(1), + payload=GroupValueResponse(DPTBinary(1)), source_address=IndividualAddress("1.3.1"), ), ) @@ -270,7 +272,7 @@ def test_maximum_apci(self): """Test parsing and streaming CEMIFrame KNX/IP packet, testing maximum APCI.""" telegram = Telegram( destination_address=GroupAddress(337), - payload=DPTBinary(DPTBinary.APCI_MAX_VALUE), + payload=GroupValueWrite(DPTBinary(DPTBinary.APCI_MAX_VALUE)), source_address=IndividualAddress("1.3.1"), ) xknx = XKNX() diff --git a/test/knxip_tests/tunnelling_request_test.py b/test/knxip_tests/tunnelling_request_test.py index d13eca7ad..8cbc92d57 100644 --- a/test/knxip_tests/tunnelling_request_test.py +++ b/test/knxip_tests/tunnelling_request_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import CouldNotParseKNXIP from xknx.knxip import CEMIFrame, KNXIPFrame, KNXIPServiceType, TunnellingRequest from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class Test_KNXIP_TunnelingReq(unittest.TestCase): @@ -59,13 +60,17 @@ def test_connect_request(self): self.assertEqual( knxipframe.body.cemi.telegram, - Telegram(destination_address=GroupAddress("9/0/8"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("9/0/8"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) knxipframe2 = KNXIPFrame(xknx) knxipframe2.init(KNXIPServiceType.TUNNELLING_REQUEST) knxipframe2.body.cemi.telegram = Telegram( - destination_address=GroupAddress("9/0/8"), payload=DPTBinary(1) + destination_address=GroupAddress("9/0/8"), + payload=GroupValueWrite(DPTBinary(1)), ) knxipframe2.body.sequence_counter = 23 knxipframe2.normalize() diff --git a/test/remote_value_tests/remote_value_1count_test.py b/test/remote_value_tests/remote_value_1count_test.py index bcf5b4112..ab4a9052b 100644 --- a/test/remote_value_tests/remote_value_1count_test.py +++ b/test/remote_value_tests/remote_value_1count_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import CouldNotParseTelegram from xknx.remote_value import RemoteValue1Count from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValue1Count(unittest.TestCase): @@ -43,7 +44,8 @@ def test_set(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x64,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x64,))), ), ) self.loop.run_until_complete( @@ -56,7 +58,8 @@ def test_set(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x65,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x65,))), ), ) @@ -65,7 +68,8 @@ def test_process(self): xknx = XKNX() remote_value = RemoteValue1Count(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x64,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x64,))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, 100) @@ -76,16 +80,19 @@ def test_to_process_error(self): remote_value = RemoteValue1Count(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x65, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x65, + ) ) ), ) diff --git a/test/remote_value_tests/remote_value_climate_mode_test.py b/test/remote_value_tests/remote_value_climate_mode_test.py index 2f745e363..4ce93b59a 100644 --- a/test/remote_value_tests/remote_value_climate_mode_test.py +++ b/test/remote_value_tests/remote_value_climate_mode_test.py @@ -13,6 +13,7 @@ ) from xknx.remote_value.remote_value_climate_mode import _RemoteValueBinaryClimateMode from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueDptValue1Ucount(unittest.TestCase): @@ -178,7 +179,8 @@ def test_set_operation_mode(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x03,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x03,))), ), ) self.loop.run_until_complete( @@ -189,7 +191,8 @@ def test_set_operation_mode(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x04,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x04,))), ), ) @@ -207,7 +210,8 @@ def test_set_binary(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(True) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(True)), ), ) self.loop.run_until_complete( @@ -218,7 +222,8 @@ def test_set_binary(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(False) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(False)), ), ) @@ -231,7 +236,8 @@ def test_process_operation_mode(self): climate_mode_type=RemoteValueClimateMode.ClimateModeType.HVAC_MODE, ) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x00,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x00,))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, HVACOperationMode.AUTO) @@ -245,7 +251,8 @@ def test_process_binary(self): operation_mode=HVACOperationMode.FROST_PROTECTION, ) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(True) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(True)), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, HVACOperationMode.FROST_PROTECTION) @@ -260,16 +267,19 @@ def test_to_process_error_operation_mode(self): ) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x65, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x65, + ) ) ), ) @@ -285,16 +295,19 @@ def test_to_process_error_heat_cool(self): ) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x01,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x01,))), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x65, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x65, + ) ) ), ) diff --git a/test/remote_value_tests/remote_value_color_rgb_test.py b/test/remote_value_tests/remote_value_color_rgb_test.py index e6c33dd02..6c1026801 100644 --- a/test/remote_value_tests/remote_value_color_rgb_test.py +++ b/test/remote_value_tests/remote_value_color_rgb_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueColorRGB from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueColorRGB(unittest.TestCase): @@ -66,7 +67,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x66)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66))), ), ) self.loop.run_until_complete(remote_value.set((100, 101, 104))) @@ -76,7 +77,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x68)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x68))), ), ) @@ -86,7 +87,7 @@ def test_process(self): remote_value = RemoteValueColorRGB(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x66)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, (100, 101, 102)) @@ -97,12 +98,13 @@ def test_to_process_error(self): remote_value = RemoteValueColorRGB(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x66, 0x67)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66, 0x67))), ) self.loop.run_until_complete(remote_value.process(telegram)) diff --git a/test/remote_value_tests/remote_value_color_rgbw_test.py b/test/remote_value_tests/remote_value_color_rgbw_test.py index 5b929b23b..5eba8f4fa 100644 --- a/test/remote_value_tests/remote_value_color_rgbw_test.py +++ b/test/remote_value_tests/remote_value_color_rgbw_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueColorRGBW from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueColorRGBW(unittest.TestCase): @@ -94,7 +95,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x66, 0x67, 0x00, 0x0F)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66, 0x67, 0x00, 0x0F))), ), ) self.loop.run_until_complete(remote_value.set((100, 101, 104, 105))) @@ -104,7 +105,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x68, 0x69, 0x00, 0x0F)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x68, 0x69, 0x00, 0x0F))), ), ) @@ -114,7 +115,7 @@ def test_process(self): remote_value = RemoteValueColorRGBW(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x66, 0x67, 0x00, 0x0F)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66, 0x67, 0x00, 0x0F))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, [100, 101, 102, 103]) @@ -125,18 +126,21 @@ def test_to_process_error(self): remote_value = RemoteValueColorRGBW(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x64, 0x65, 0x66)), + payload=GroupValueWrite(DPTArray((0x64, 0x65, 0x66))), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x00, 0x00, 0x0F, 0x64, 0x65, 0x66, 0x67)), + payload=GroupValueWrite( + DPTArray((0x00, 0x00, 0x0F, 0x64, 0x65, 0x66, 0x67)) + ), ) self.loop.run_until_complete(remote_value.process(telegram)) diff --git a/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py b/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py index e34c8dcc0..ffcd3f88d 100644 --- a/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py +++ b/test/remote_value_tests/remote_value_dpt_2_byte_unsigned_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueDpt2ByteUnsigned from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueDptValue2Ucount(unittest.TestCase): @@ -55,7 +56,7 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((0x0A, 0x0B)), + payload=GroupValueWrite(DPTArray((0x0A, 0x0B))), ), ) self.loop.run_until_complete(remote_value.set(5500)) @@ -65,10 +66,12 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x15, - 0x7C, + payload=GroupValueWrite( + DPTArray( + ( + 0x15, + 0x7C, + ) ) ), ), @@ -81,7 +84,8 @@ def test_process(self): xknx, group_address=GroupAddress("1/2/3") ) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A, 0x0B)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0A, 0x0B))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, 2571) @@ -94,22 +98,26 @@ def test_to_process_error(self): ) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x64,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x64,))), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x53, - 0x42, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x53, + 0x42, + ) ) ), ) diff --git a/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py b/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py index 5f3ae1eb0..dbf5d2302 100644 --- a/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py +++ b/test/remote_value_tests/remote_value_dpt_value_1_ucount_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueDptValue1Ucount from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueDptValue1Ucount(unittest.TestCase): @@ -54,7 +55,8 @@ def test_set(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0A,))), ), ) self.loop.run_until_complete(remote_value.set(11)) @@ -63,7 +65,8 @@ def test_set(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0B,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0B,))), ), ) @@ -74,7 +77,8 @@ def test_process(self): xknx, group_address=GroupAddress("1/2/3") ) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0A,))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, 10) @@ -87,16 +91,19 @@ def test_to_process_error(self): ) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x65, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x65, + ) ) ), ) diff --git a/test/remote_value_tests/remote_value_scene_number_test.py b/test/remote_value_tests/remote_value_scene_number_test.py index a356bd7c0..303e12db9 100644 --- a/test/remote_value_tests/remote_value_scene_number_test.py +++ b/test/remote_value_tests/remote_value_scene_number_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueSceneNumber from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueSceneNumber(unittest.TestCase): @@ -52,7 +53,8 @@ def test_set(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0A,))), ), ) self.loop.run_until_complete(remote_value.set(12)) @@ -61,7 +63,8 @@ def test_set(self): self.assertEqual( telegram, Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0B,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0B,))), ), ) @@ -70,7 +73,8 @@ def test_process(self): xknx = XKNX() remote_value = RemoteValueSceneNumber(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A,)) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray((0x0A,))), ) self.loop.run_until_complete(remote_value.process(telegram)) self.assertEqual(remote_value.value, 11) @@ -81,16 +85,19 @@ def test_to_process_error(self): remote_value = RemoteValueSceneNumber(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x65, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x65, + ) ) ), ) diff --git a/test/remote_value_tests/remote_value_step_test.py b/test/remote_value_tests/remote_value_step_test.py index 5a326954e..6f8f923be 100644 --- a/test/remote_value_tests/remote_value_step_test.py +++ b/test/remote_value_tests/remote_value_step_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueStep from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueStep(unittest.TestCase): @@ -81,14 +82,20 @@ def test_set(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) self.loop.run_until_complete(remote_value.increase()) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) def test_process(self): @@ -96,7 +103,8 @@ def test_process(self): xknx = XKNX() remote_value = RemoteValueStep(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.assertEqual(remote_value.value, None) self.loop.run_until_complete(remote_value.process(telegram)) @@ -108,12 +116,14 @@ def test_to_process_error(self): remote_value = RemoteValueStep(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray(0x01) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(0x01)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(3) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(3)), ) self.loop.run_until_complete(remote_value.process(telegram)) # pylint: disable=pointless-statement diff --git a/test/remote_value_tests/remote_value_string_test.py b/test/remote_value_tests/remote_value_string_test.py index 78ac49491..f34b47db1 100644 --- a/test/remote_value_tests/remote_value_string_test.py +++ b/test/remote_value_tests/remote_value_string_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueString from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueString(unittest.TestCase): @@ -93,7 +94,9 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((97, 115, 100, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), + payload=GroupValueWrite( + DPTArray((97, 115, 100, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + ), ), ) self.loop.run_until_complete(remote_value.set("ASDF")) @@ -103,7 +106,9 @@ def test_set(self): telegram, Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray((65, 83, 68, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), + payload=GroupValueWrite( + DPTArray((65, 83, 68, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + ), ), ) @@ -113,22 +118,24 @@ def test_process(self): remote_value = RemoteValueString(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x41, - 0x41, - 0x41, - 0x41, - 0x41, - 0x42, - 0x42, - 0x42, - 0x42, - 0x42, - 0x43, - 0x43, - 0x43, - 0x43, + payload=GroupValueWrite( + DPTArray( + ( + 0x41, + 0x41, + 0x41, + 0x41, + 0x41, + 0x42, + 0x42, + 0x42, + 0x42, + 0x42, + 0x43, + 0x43, + 0x43, + 0x43, + ) ) ), ) @@ -141,16 +148,19 @@ def test_to_process_error(self): remote_value = RemoteValueString(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( destination_address=GroupAddress("1/2/3"), - payload=DPTArray( - ( - 0x64, - 0x65, + payload=GroupValueWrite( + DPTArray( + ( + 0x64, + 0x65, + ) ) ), ) diff --git a/test/remote_value_tests/remote_value_switch_test.py b/test/remote_value_tests/remote_value_switch_test.py index 325dc0426..bd088a7a6 100644 --- a/test/remote_value_tests/remote_value_switch_test.py +++ b/test/remote_value_tests/remote_value_switch_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueSwitch from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueSwitch(unittest.TestCase): @@ -65,14 +66,20 @@ def test_set(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) self.loop.run_until_complete(remote_value.off()) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) def test_process(self): @@ -80,7 +87,8 @@ def test_process(self): xknx = XKNX() remote_value = RemoteValueSwitch(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.assertEqual(remote_value.value, None) self.loop.run_until_complete(remote_value.process(telegram)) @@ -92,7 +100,8 @@ def test_process_off(self): xknx = XKNX() remote_value = RemoteValueSwitch(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), ) self.assertEqual(remote_value.value, None) self.loop.run_until_complete(remote_value.process(telegram)) @@ -105,12 +114,14 @@ def test_to_process_error(self): remote_value = RemoteValueSwitch(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray(0x01) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(0x01)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(3) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(3)), ) self.loop.run_until_complete(remote_value.process(telegram)) # pylint: disable=pointless-statement diff --git a/test/remote_value_tests/remote_value_test.py b/test/remote_value_tests/remote_value_test.py index 44c528a0c..4ac9071a6 100644 --- a/test/remote_value_tests/remote_value_test.py +++ b/test/remote_value_tests/remote_value_test.py @@ -8,6 +8,7 @@ from xknx.exceptions import CouldNotParseTelegram from xknx.remote_value import RemoteValue, RemoteValueSwitch from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValue(unittest.TestCase): @@ -84,6 +85,26 @@ def test_default_value_unit(self): remote_value = RemoteValue(xknx) self.assertEqual(remote_value.unit_of_measurement, None) + def test_process_unsupported_payload(self): + """Test if exception is raised when processing telegram with unsupported payload.""" + xknx = XKNX() + remote_value = RemoteValue(xknx) + with patch("xknx.remote_value.RemoteValue.payload_valid") as patch_valid, patch( + "xknx.remote_value.RemoteValue.has_group_address" + ) as patch_has_group_address: + patch_valid.return_value = False + patch_has_group_address.return_value = True + + telegram = Telegram( + destination_address=GroupAddress("1/2/1"), + payload=object(), + ) + with self.assertRaisesRegex( + CouldNotParseTelegram, + r".*payload not a GroupValueWrite or GroupValueResponse.*", + ): + self.loop.run_until_complete(remote_value.process(telegram)) + def test_process_invalid_payload(self): """Test if exception is raised when processing telegram with invalid payload.""" xknx = XKNX() @@ -96,9 +117,9 @@ def test_process_invalid_payload(self): telegram = Telegram( destination_address=GroupAddress("1/2/1"), - payload=DPTArray((0x01, 0x02)), + payload=GroupValueWrite(DPTArray((0x01, 0x02))), ) - with self.assertRaises(CouldNotParseTelegram): + with self.assertRaisesRegex(CouldNotParseTelegram, r".*payload invalid.*"): self.loop.run_until_complete(remote_value.process(telegram)) def test_read_state(self): @@ -113,7 +134,8 @@ def test_read_state(self): fut = asyncio.Future() telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) fut.set_result(telegram) patch_read.return_value = fut @@ -158,7 +180,8 @@ def test_process_listening_address(self): patch_valid.return_value = True test_payload = DPTArray((0x01, 0x02)) telegram = Telegram( - destination_address=GroupAddress("1/1/1"), payload=test_payload + destination_address=GroupAddress("1/1/1"), + payload=GroupValueWrite(test_payload), ) self.assertTrue( self.loop.run_until_complete( diff --git a/test/remote_value_tests/remote_value_updown_test.py b/test/remote_value_tests/remote_value_updown_test.py index 303c9fb45..04483779a 100644 --- a/test/remote_value_tests/remote_value_updown_test.py +++ b/test/remote_value_tests/remote_value_updown_test.py @@ -7,6 +7,7 @@ from xknx.exceptions import ConversionError, CouldNotParseTelegram from xknx.remote_value import RemoteValueUpDown from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueWrite class TestRemoteValueUpDown(unittest.TestCase): @@ -81,14 +82,20 @@ def test_set(self): telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), + ), ) self.loop.run_until_complete(remote_value.up()) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, - Telegram(destination_address=GroupAddress("1/2/3"), payload=DPTBinary(0)), + Telegram( + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(0)), + ), ) def test_process(self): @@ -96,7 +103,8 @@ def test_process(self): xknx = XKNX() remote_value = RemoteValueUpDown(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(1) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(1)), ) self.assertEqual(remote_value.value, None) self.loop.run_until_complete(remote_value.process(telegram)) @@ -108,12 +116,14 @@ def test_to_process_error(self): remote_value = RemoteValueUpDown(xknx, group_address=GroupAddress("1/2/3")) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTArray(0x01) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTArray(0x01)), ) self.loop.run_until_complete(remote_value.process(telegram)) with self.assertRaises(CouldNotParseTelegram): telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(3) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(3)), ) self.loop.run_until_complete(remote_value.process(telegram)) # pylint: disable=pointless-statement diff --git a/test/str_test.py b/test/str_test.py index 7972402fe..2d95776a6 100644 --- a/test/str_test.py +++ b/test/str_test.py @@ -56,6 +56,7 @@ ) from xknx.remote_value import RemoteValue from xknx.telegram import GroupAddress, IndividualAddress, Telegram, TelegramDirection +from xknx.telegram.apci import GroupValueWrite # pylint: disable=too-many-public-methods,invalid-name @@ -283,7 +284,7 @@ def test_sensor(self): telegram = Telegram( destination_address=GroupAddress("1/2/3"), direction=TelegramDirection.INCOMING, - payload=DPTArray(0x40), + payload=GroupValueWrite(DPTArray(0x40)), ) self.loop.run_until_complete(sensor.process_group_write(telegram)) self.assertEqual( @@ -352,7 +353,7 @@ def test_weather(self): telegram = Telegram( destination_address=GroupAddress("7/0/10"), direction=TelegramDirection.INCOMING, - payload=DPTBinary(1), + payload=GroupValueWrite(DPTBinary(1)), ) self.loop.run_until_complete(weather.process_group_write(telegram)) @@ -469,12 +470,13 @@ def test_dpt_binary(self): def test_telegram(self): """Test string representation of Telegram.""" telegram = Telegram( - destination_address=GroupAddress("1/2/3"), payload=DPTBinary(7) + destination_address=GroupAddress("1/2/3"), + payload=GroupValueWrite(DPTBinary(7)), ) self.assertEqual( str(telegram), - '" />', + '" />" />', ) def test_dib_generic(self): @@ -642,7 +644,7 @@ def test_tunnelling_request(self): self.assertEqual( str(tunnelling_request), '" />', + ' DestinationAddress="GroupAddress("0/0/0")" Flags=" 0" payload="None" />" />', ) def test_tunnelling_ack(self): @@ -662,12 +664,13 @@ def test_cemi_frame(self): cemi_frame = CEMIFrame(xknx) cemi_frame.src_addr = GroupAddress("1/2/3") cemi_frame.telegram = Telegram( - destination_address=GroupAddress("1/2/5"), payload=DPTBinary(7) + destination_address=GroupAddress("1/2/5"), + payload=GroupValueWrite(DPTBinary(7)), ) self.assertEqual( str(cemi_frame), - '" />', + '" />" />', ) def test_knxip_frame(self): @@ -710,5 +713,5 @@ def test_routing_indication_str(self): ri = RoutingIndication(xknx) self.assertEqual( str(ri), - '" />', + '" />', ) diff --git a/test/telegram_tests/apci_test.py b/test/telegram_tests/apci_test.py new file mode 100644 index 000000000..7cb42ea80 --- /dev/null +++ b/test/telegram_tests/apci_test.py @@ -0,0 +1,172 @@ +"""Unit test for APCI objects.""" +import unittest + +from pytest import raises +from xknx.dpt import DPTArray, DPTBinary +from xknx.exceptions import ConversionError +from xknx.telegram.apci import APCI, GroupValueRead, GroupValueResponse, GroupValueWrite + + +class TestAPCI(unittest.TestCase): + """Test class for APCI objects.""" + + def test_resolve_apci(self): + """Test resolve_apci for supported services.""" + test_cases = [ + (GroupValueRead.code.value, GroupValueRead), + (GroupValueWrite.code.value, GroupValueWrite), + (GroupValueResponse.code.value, GroupValueResponse), + ] + + for code, clazz in test_cases: + self.assertIsInstance(APCI.resolve_apci(code), clazz) + + def test_resolve_apci_unsupported(self): + """Test resolve_apci for unsupported services.""" + + with raises(ConversionError, match=r".*Class not implemented for APCI.*"): + APCI.resolve_apci(0x0100) + + with raises( + ConversionError, match=r".*Class not implemented for extended APCI.*" + ): + APCI.resolve_apci(0x03C0) + + +class TestGroupValueRead(unittest.TestCase): + """Test class for GroupValueRead objects.""" + + def test_calculated_length(self): + """Test the test_calculated_length method.""" + payload = GroupValueRead() + + self.assertEqual(payload.calculated_length(), 1) + + def test_from_knx(self): + """Test the from_knx method.""" + payload = GroupValueRead() + payload.from_knx(bytes([0x00, 0x00])) + + self.assertEqual(payload, GroupValueRead()) + + def test_to_knx(self): + """Test the to_knx method.""" + payload = GroupValueRead() + + self.assertEqual(payload.to_knx(), bytes([0x00, 0x00])) + + def test_str(self): + """Test the __str__ method.""" + payload = GroupValueRead() + + self.assertEqual(str(payload), "") + + +class TestGroupValueWrite(unittest.TestCase): + """Test class for GroupValueWrite objects.""" + + def test_calculated_length(self): + """Test the test_calculated_length method.""" + payload_a = GroupValueWrite(DPTArray((0x01, 0x02, 0x03))) + payload_b = GroupValueWrite(DPTBinary(1)) + + self.assertEqual(payload_a.calculated_length(), 4) + self.assertEqual(payload_b.calculated_length(), 1) + + def test_calculated_length_exception(self): + """Test the test_calculated_length method for unsupported dpt.""" + payload = GroupValueWrite(object()) + + with self.assertRaises(TypeError): + payload.calculated_length() + + def test_from_knx(self): + """Test the from_knx method.""" + payload_a = GroupValueWrite() + payload_a.from_knx(bytes([0x00, 0x80, 0x05, 0x04, 0x03, 0x02, 0x01])) + payload_b = GroupValueWrite() + payload_b.from_knx(bytes([0x00, 0x82])) + + self.assertEqual( + payload_a, GroupValueWrite(DPTArray((0x05, 0x04, 0x03, 0x02, 0x01))) + ) + self.assertEqual(payload_b, GroupValueWrite(DPTBinary(0x02))) + + def test_to_knx(self): + """Test the to_knx method.""" + + payload_a = GroupValueWrite(DPTArray((0x01, 0x02, 0x03))) + payload_b = GroupValueWrite(DPTBinary(1)) + + self.assertEqual(payload_a.to_knx(), bytes([0x00, 0x80, 0x01, 0x02, 0x03])) + self.assertEqual(payload_b.to_knx(), bytes([0x00, 0x81])) + + def test_to_knx_exception(self): + """Test the to_knx method for unsupported dpt.""" + payload = GroupValueWrite(object()) + + with self.assertRaises(TypeError): + payload.to_knx() + + def test_str(self): + """Test the __str__ method.""" + payload = GroupValueWrite(DPTBinary(1)) + + self.assertEqual( + str(payload), '" />' + ) + + +class TestGroupValueResponse(unittest.TestCase): + """Test class for TestGroupValueResponse objects.""" + + def test_calculated_length(self): + """Test the test_calculated_length method.""" + payload_a = GroupValueResponse(DPTArray((0x01, 0x02, 0x03))) + payload_b = GroupValueResponse(DPTBinary(1)) + + self.assertEqual(payload_a.calculated_length(), 4) + self.assertEqual(payload_b.calculated_length(), 1) + + def test_calculated_length_exception(self): + """Test the test_calculated_length method for unsupported dpt.""" + payload = GroupValueResponse(object()) + + with self.assertRaises(TypeError): + payload.calculated_length() + + def test_from_knx(self): + """Test the from_knx method.""" + payload_a = GroupValueResponse() + payload_a.from_knx(bytes([0x00, 0x80, 0x05, 0x04, 0x03, 0x02, 0x01])) + payload_b = GroupValueResponse() + payload_b.from_knx(bytes([0x00, 0x82])) + + self.assertEqual( + payload_a, GroupValueResponse(DPTArray((0x05, 0x04, 0x03, 0x02, 0x01))) + ) + self.assertEqual(payload_b, GroupValueResponse(DPTBinary(0x02))) + + def test_to_knx(self): + """Test the to_knx method.""" + + payload_a = GroupValueResponse(DPTArray((0x01, 0x02, 0x03))) + payload_b = GroupValueResponse(DPTBinary(1)) + + self.assertEqual(payload_a.to_knx(), bytes([0x00, 0x40, 0x01, 0x02, 0x03])) + self.assertEqual(payload_b.to_knx(), bytes([0x00, 0x41])) + + def test_to_knx_exception(self): + """Test the to_knx method for unsupported dpt.""" + payload = GroupValueResponse(object()) + + with self.assertRaises(TypeError): + payload.to_knx() + + def test_str(self): + """Test the __str__ method.""" + payload = GroupValueResponse(DPTBinary(1)) + + self.assertEqual( + str(payload), '" />' + ) diff --git a/test/telegram_tests/telegram_test.py b/test/telegram_tests/telegram_test.py index 37e759935..d41c8949b 100644 --- a/test/telegram_tests/telegram_test.py +++ b/test/telegram_tests/telegram_test.py @@ -1,7 +1,9 @@ """Unit test for Telegram objects.""" import unittest -from xknx.telegram import GroupAddress, Telegram, TelegramDirection, TelegramType +from xknx.dpt import DPTBinary +from xknx.telegram import GroupAddress, Telegram, TelegramDirection +from xknx.telegram.apci import GroupValueRead, GroupValueWrite class TestTelegram(unittest.TestCase): @@ -13,25 +15,25 @@ class TestTelegram(unittest.TestCase): def test_telegram_equal(self): """Test equals operator.""" self.assertEqual( - Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), - Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), + Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), + Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), ) def test_telegram_not_equal(self): """Test not equals operator.""" self.assertNotEqual( - Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), - Telegram(GroupAddress("1/2/4"), TelegramType.GROUP_READ), + Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), + Telegram(GroupAddress("1/2/4"), payload=GroupValueRead()), ) self.assertNotEqual( - Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), - Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_WRITE), + Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), + Telegram(GroupAddress("1/2/3"), payload=GroupValueWrite(DPTBinary(1))), ) self.assertNotEqual( - Telegram(GroupAddress("1/2/3"), TelegramType.GROUP_READ), + Telegram(GroupAddress("1/2/3"), payload=GroupValueRead()), Telegram( GroupAddress("1/2/3"), - TelegramType.GROUP_READ, TelegramDirection.INCOMING, + payload=GroupValueRead(), ), ) diff --git a/xknx/core/telegram_queue.py b/xknx/core/telegram_queue.py index 5d8eb94f7..5981bdd4f 100644 --- a/xknx/core/telegram_queue.py +++ b/xknx/core/telegram_queue.py @@ -11,7 +11,8 @@ import logging from xknx.exceptions import CommunicationError, XKNXException -from xknx.telegram import TelegramDirection, TelegramType +from xknx.telegram import TelegramDirection +from xknx.telegram.apci import GroupValueWrite logger = logging.getLogger("xknx.log") telegram_logger = logging.getLogger("xknx.telegram") @@ -133,7 +134,7 @@ async def process_telegram_outgoing(self, telegram): telegram_logger.debug(telegram) if self.xknx.knxip_interface is not None: await self.xknx.knxip_interface.send_telegram(telegram) - if telegram.telegramtype == TelegramType.GROUP_WRITE: + if isinstance(telegram.payload, GroupValueWrite): await self.xknx.devices.process(telegram) else: logger.warning("No KNXIP interface defined") diff --git a/xknx/core/value_reader.py b/xknx/core/value_reader.py index 6c3ea6f62..928d05d9a 100644 --- a/xknx/core/value_reader.py +++ b/xknx/core/value_reader.py @@ -10,7 +10,8 @@ import asyncio import logging -from xknx.telegram import Telegram, TelegramType +from xknx.telegram import Telegram +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite logger = logging.getLogger("xknx.log") @@ -49,19 +50,14 @@ async def read(self): async def send_group_read(self): """Send group read.""" telegram = Telegram( - destination_address=self.group_address, telegramtype=TelegramType.GROUP_READ + destination_address=self.group_address, payload=GroupValueRead() ) await self.xknx.telegrams.put(telegram) async def telegram_received(self, telegram): """Test if telegram has correct group address and trigger event.""" - if ( - telegram.destination_address == self.group_address - and telegram.telegramtype - in ( - TelegramType.GROUP_RESPONSE, - TelegramType.GROUP_WRITE, - ) + if telegram.destination_address == self.group_address and isinstance( + telegram.payload, (GroupValueResponse, GroupValueWrite) ): self.success = True self.received_telegram = telegram diff --git a/xknx/devices/device.py b/xknx/devices/device.py index 6d33b2e7e..72c44bb55 100644 --- a/xknx/devices/device.py +++ b/xknx/devices/device.py @@ -5,7 +5,7 @@ """ import logging -from xknx.telegram import TelegramType +from xknx.telegram.apci import GroupValueRead, GroupValueResponse, GroupValueWrite logger = logging.getLogger("xknx.log") @@ -51,11 +51,11 @@ async def sync(self, wait_for_result=False): async def process(self, telegram): """Process incoming telegram.""" - if telegram.telegramtype == TelegramType.GROUP_WRITE: + if isinstance(telegram.payload, GroupValueWrite): await self.process_group_write(telegram) - elif telegram.telegramtype == TelegramType.GROUP_RESPONSE: + elif isinstance(telegram.payload, GroupValueResponse): await self.process_group_response(telegram) - elif telegram.telegramtype == TelegramType.GROUP_READ: + elif isinstance(telegram.payload, GroupValueRead): await self.process_group_read(telegram) async def process_group_read(self, telegram): @@ -69,7 +69,7 @@ async def process_group_response(self, telegram): async def process_group_write(self, telegram): """Process incoming GroupValueWrite telegrams.""" - # The dafault is, that devices dont process group writes + # The default is, that devices dont process group writes def get_name(self): """Return name of device.""" diff --git a/xknx/dpt/__init__.py b/xknx/dpt/__init__.py index b5bfff829..0141d90e0 100644 --- a/xknx/dpt/__init__.py +++ b/xknx/dpt/__init__.py @@ -170,3 +170,161 @@ from .dpt_scaling import DPTAngle, DPTScaling from .dpt_string import DPTString from .dpt_time import DPTTime + +__all__ = [ + "DPT2ByteFloat", + "DPT2ByteSigned", + "DPT2ByteUnsigned", + "DPT2Ucount", + "DPT4ByteFloat", + "DPT4ByteSigned", + "DPT4ByteUnsigned", + "DPTAbsoluteTemperature", + "DPTAcceleration", + "DPTAccelerationAngular", + "DPTActivationEnergy", + "DPTActiveEnergy", + "DPTActiveEnergykWh", + "DPTActivity", + "DPTAmplitude", + "DPTAngle", + "DPTAngleDeg", + "DPTAngleRad", + "DPTAngularFrequency", + "DPTAngularMomentum", + "DPTAngularVelocity", + "DPTApparantEnergy", + "DPTApparantEnergykVAh", + "DPTArea", + "DPTArray", + "DPTBase", + "DPTBinary", + "DPTBrightness", + "DPTCapacitance", + "DPTChargeDensitySurface", + "DPTChargeDensityVolume", + "DPTColorTemperature", + "DPTCommonTemperature", + "DPTComparator", + "DPTCompressibility", + "DPTConductance", + "DPTControllerStatus", + "DPTCurrent", + "DPTDate", + "DPTDateTime", + "DPTDecimalFactor", + "DPTDeltaTimeHrs", + "DPTDeltaTimeMin", + "DPTDeltaTimeMsec", + "DPTDeltaTimeSec", + "DPTDensity", + "DPTElectricalConductivity", + "DPTElectricCharge", + "DPTElectricCurrent", + "DPTElectricCurrentDensity", + "DPTElectricDipoleMoment", + "DPTElectricDisplacement", + "DPTElectricFieldStrength", + "DPTElectricFlux", + "DPTElectricFluxDensity", + "DPTElectricPolarization", + "DPTElectricPotential", + "DPTElectricPotentialDifference", + "DPTElectromagneticMoment", + "DPTElectromotiveForce", + "DPTEnergy", + "DPTEnthalpy", + "DPTFlowRateM3H", + "DPTForce", + "DPTFrequency", + "DPTHeatCapacity", + "DPTHeatFlowRate", + "DPTHeatQuantity", + "DPTHumidity", + "DPTHVACContrMode", + "DPTHVACMode", + "DPTImpedance", + "DPTKelvinPerPercent", + "DPTLength", + "DPTLengthMm", + "DPTLightQuantity", + "DPTLongDeltaTimeSec", + "DPTLuminance", + "DPTLuminousFlux", + "DPTLuminousIntensity", + "DPTLux", + "DPTMagneticFieldStrength", + "DPTMagneticFlux", + "DPTMagneticFluxDensity", + "DPTMagneticMoment", + "DPTMagneticPolarization", + "DPTMagnetization", + "DPTMagnetomotiveForce", + "DPTMass", + "DPTMassFlux", + "DPTMol", + "DPTMomentum", + "DPTPartsPerMillion", + "DPTPercentU8", + "DPTPercentV16", + "DPTPercentV8", + "DPTPhaseAngleDeg", + "DPTPhaseAngleRad", + "DPTPower", + "DPTPower2Byte", + "DPTPowerDensity", + "DPTPowerFactor", + "DPTPressure", + "DPTPressure2Byte", + "DPTRainAmount", + "DPTReactance", + "DPTReactiveEnergy", + "DPTReactiveEnergykVARh", + "DPTResistance", + "DPTResistivity", + "DPTRotationAngle", + "DPTScaling", + "DPTSceneNumber", + "DPTSelfInductance", + "DPTSignedRelativeValue", + "DPTSolidAngle", + "DPTSoundIntensity", + "DPTSpeed", + "DPTStress", + "DPTString", + "DPTSurfaceTension", + "DPTTariff", + "DPTTemperature", + "DPTTemperatureA", + "DPTTemperatureDifference", + "DPTTemperatureDifference2Byte", + "DPTTemperatureF", + "DPTThermalCapacity", + "DPTThermalConductivity", + "DPTThermoelectricPower", + "DPTTime", + "DPTTime1", + "DPTTime2", + "DPTTimePeriod100Msec", + "DPTTimePeriod10Msec", + "DPTTimePeriodHrs", + "DPTTimePeriodMin", + "DPTTimePeriodMsec", + "DPTTimePeriodSec", + "DPTTimeSeconds", + "DPTTorque", + "DPTUElCurrentmA", + "DPTValue1ByteUnsigned", + "DPTValue1Count", + "DPTValue1Ucount", + "DPTValue2Count", + "DPTValue4Count", + "DPTVoltage", + "DPTVolume", + "DPTVolumeFlow", + "DPTVolumeFlux", + "DPTWeight", + "DPTWork", + "DPTWsp", + "DPTWspKmh", +] diff --git a/xknx/dpt/dpt.py b/xknx/dpt/dpt.py index f462ddca0..c6c42abfa 100644 --- a/xknx/dpt/dpt.py +++ b/xknx/dpt/dpt.py @@ -1,4 +1,6 @@ """Implementation of Basic KNX datatypes.""" +from typing import Union + from xknx.exceptions import ConversionError @@ -126,7 +128,7 @@ class DPTBinary: APCI_BITMASK = 0x3F APCI_MAX_VALUE = APCI_BITMASK - def __init__(self, value): + def __init__(self, value) -> None: """Initialize DPTBinary class.""" if not isinstance(value, int): raise TypeError() @@ -148,7 +150,7 @@ class DPTArray: """The DPTArray is a base class for all datatypes appended to the KNX telegram.""" # pylint: disable=too-few-public-methods - def __init__(self, value): + def __init__(self, value: Union[int, list, bytes, tuple]) -> None: """Initialize DPTArray class.""" if isinstance(value, int): self.value = (value,) diff --git a/xknx/io/routing.py b/xknx/io/routing.py index 57776ffb8..3fbd4aa0d 100644 --- a/xknx/io/routing.py +++ b/xknx/io/routing.py @@ -6,7 +6,6 @@ import logging from xknx.knxip import ( - APCICommand, CEMIFrame, CEMIMessageCode, KNXIPFrame, @@ -49,12 +48,6 @@ def response_rec_callback(self, knxipframe, _): return elif knxipframe.body.cemi.src_addr == self.xknx.own_address: logger.debug("Ignoring own packet") - elif knxipframe.body.cemi.cmd not in ( - APCICommand.GROUP_READ, - APCICommand.GROUP_WRITE, - APCICommand.GROUP_RESPONSE, - ): - logger.warning("APCI not implemented: %s", knxipframe) else: telegram = knxipframe.body.cemi.telegram telegram.direction = TelegramDirection.INCOMING diff --git a/xknx/knxip/__init__.py b/xknx/knxip/__init__.py index cb8edbaf5..0b1d000b9 100644 --- a/xknx/knxip/__init__.py +++ b/xknx/knxip/__init__.py @@ -14,7 +14,6 @@ from .hpai import HPAI from .knxip import KNXIPFrame from .knxip_enum import ( - APCICommand, CEMIFlags, CEMIMessageCode, ConnectRequestType, diff --git a/xknx/knxip/cemi_frame.py b/xknx/knxip/cemi_frame.py index c26dd1e64..f3c820396 100644 --- a/xknx/knxip/cemi_frame.py +++ b/xknx/knxip/cemi_frame.py @@ -13,11 +13,11 @@ """ from typing import Union -from xknx.dpt import DPTArray, DPTBinary from xknx.exceptions import ConversionError, CouldNotParseKNXIP, UnsupportedCEMIMessage -from xknx.telegram import GroupAddress, IndividualAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, IndividualAddress, Telegram +from xknx.telegram.apci import APCI -from .knxip_enum import APCICommand, CEMIFlags, CEMIMessageCode +from .knxip_enum import CEMIFlags, CEMIMessageCode class CEMIFrame: @@ -30,7 +30,6 @@ def __init__( xknx, code: CEMIMessageCode = CEMIMessageCode.L_DATA_IND, flags: int = 0, - cmd: APCICommand = APCICommand.GROUP_READ, src_addr: IndividualAddress = IndividualAddress(None), dst_addr: Union[GroupAddress, IndividualAddress] = GroupAddress(None), mpdu_len: int = 0, @@ -40,7 +39,6 @@ def __init__( self.xknx = xknx self.code = code self.flags = flags - self.cmd = cmd self.src_addr = src_addr self.dst_addr = dst_addr self.mpdu_len = mpdu_len @@ -63,19 +61,8 @@ def init_from_telegram( def telegram(self) -> Telegram: """Return telegram.""" - def resolve_telegram_type(cmd): - """Return telegram type from APCI Command.""" - if cmd == APCICommand.GROUP_WRITE: - return TelegramType.GROUP_WRITE - if cmd == APCICommand.GROUP_READ: - return TelegramType.GROUP_READ - if cmd == APCICommand.GROUP_RESPONSE: - return TelegramType.GROUP_RESPONSE - raise ConversionError(f"Telegram not implemented for {self.cmd}") - return Telegram( destination_address=self.dst_addr, - telegramtype=resolve_telegram_type(self.cmd), payload=self.payload, source_address=self.src_addr, ) @@ -105,19 +92,6 @@ def telegram(self, telegram: Telegram): else: raise TypeError() - # TODO: use telegram.direction - def resolve_cmd(telegramtype: TelegramType) -> APCICommand: - """Resolve APCICommand from TelegramType.""" - if telegramtype == TelegramType.GROUP_READ: - return APCICommand.GROUP_READ - if telegramtype == TelegramType.GROUP_WRITE: - return APCICommand.GROUP_WRITE - if telegramtype == TelegramType.GROUP_RESPONSE: - return APCICommand.GROUP_RESPONSE - raise TypeError() - - self.cmd = resolve_cmd(telegram.telegramtype) - def set_hops(self, hops: int): """Set hops.""" # Resetting hops @@ -127,13 +101,9 @@ def set_hops(self, hops: int): def calculated_length(self) -> int: """Get length of KNX/IP body.""" - if self.payload is None: - return 11 - if isinstance(self.payload, DPTBinary): - return 11 - if isinstance(self.payload, DPTArray): - return 11 + len(self.payload.value) - raise TypeError() + if not isinstance(self.payload, APCI): + raise TypeError() + return 10 + self.payload.calculated_length() def from_knx(self, raw) -> int: """Parse/deserialize from KNX/IP raw data.""" @@ -188,35 +158,31 @@ def from_knx_data_link_layer(self, cemi) -> int: # TPCI (transport layer control information) -> First 14 bit # APCI (application layer control information) -> Last 10 bit - tpci_apci = cemi[9 + addil] * 256 + cemi[10 + addil] - - try: - self.cmd = APCICommand(tpci_apci & 0xFFC0) - except ValueError: - raise UnsupportedCEMIMessage( - "APCI not supported: {:#012b} in CEMI: {}".format( - tpci_apci & 0xFFC0, cemi.hex() - ) - ) - - apdu = cemi[10 + addil :] - if len(apdu) != self.mpdu_len: + apdu = cemi[9 + addil :] + if len(apdu) != (self.mpdu_len + 1): raise CouldNotParseKNXIP( "APDU LEN should be {} but is {} in CEMI: {}".format( self.mpdu_len, len(apdu), cemi.hex() ) ) - if len(apdu) == 1: - apci = tpci_apci & DPTBinary.APCI_BITMASK - self.payload = DPTBinary(apci) - else: - self.payload = DPTArray(cemi[11 + addil :]) + tpci_apci = (apdu[0] << 8) + apdu[1] + + try: + self.payload = APCI.resolve_apci(tpci_apci & 0x03FF) + except ConversionError: + raise UnsupportedCEMIMessage( + "APCI not supported: {:#012b}".format(tpci_apci & 0x03FF) + ) + + self.payload.from_knx(apdu) - return 10 + addil + len(apdu) + return 10 + addil + self.mpdu_len def to_knx(self): """Serialize to KNX/IP raw data.""" + if not isinstance(self.payload, APCI): + raise TypeError() if not isinstance(self.src_addr, (GroupAddress, IndividualAddress)): raise ConversionError("src_addr not set") if not isinstance(self.dst_addr, (GroupAddress, IndividualAddress)): @@ -230,42 +196,19 @@ def to_knx(self): data.append(self.flags & 255) data.extend(self.src_addr.to_knx()) data.extend(self.dst_addr.to_knx()) + data.append(self.payload.calculated_length()) + data.extend(self.payload.to_knx()) - def encode_cmd_and_payload(cmd, encoded_payload=0, appended_payload=None): - """Encode cmd and payload.""" - if appended_payload is None: - appended_payload = [] - data = [ - 1 + len(appended_payload), - (cmd.value >> 8) & 0xFF, - (cmd.value & 0xFF) | (encoded_payload & DPTBinary.APCI_BITMASK), - ] - data.extend(appended_payload) - return data - - if self.payload is None: - data.extend(encode_cmd_and_payload(self.cmd)) - elif isinstance(self.payload, DPTBinary): - data.extend( - encode_cmd_and_payload(self.cmd, encoded_payload=self.payload.value) - ) - elif isinstance(self.payload, DPTArray): - data.extend( - encode_cmd_and_payload(self.cmd, appended_payload=self.payload.value) - ) - else: - raise TypeError() return data def __str__(self): """Return object as readable string.""" return ( ''.format( + 'Flags="{:16b}" payload="{}" />'.format( self.src_addr.__repr__(), self.dst_addr.__repr__(), self.flags, - self.cmd, self.payload, ) ) diff --git a/xknx/knxip/knxip_enum.py b/xknx/knxip/knxip_enum.py index 127a76e9e..f4d1e060c 100644 --- a/xknx/knxip/knxip_enum.py +++ b/xknx/knxip/knxip_enum.py @@ -49,14 +49,6 @@ class CEMIMessageCode(Enum): L_RAW_CON = 0x2F -class APCICommand(Enum): - """Enum class for KNX/IP APCI Commands.""" - - GROUP_READ = 0x00 - GROUP_WRITE = 0x80 - GROUP_RESPONSE = 0x40 - - class CEMIFlags: """Enum class for KNX/IP CEMI Flags.""" diff --git a/xknx/remote_value/remote_value.py b/xknx/remote_value/remote_value.py index 88f86ee31..dc7a298c3 100644 --- a/xknx/remote_value/remote_value.py +++ b/xknx/remote_value/remote_value.py @@ -10,7 +10,8 @@ from typing import List from xknx.exceptions import CouldNotParseTelegram -from xknx.telegram import GroupAddress, Telegram, TelegramType +from xknx.telegram import GroupAddress, Telegram +from xknx.telegram.apci import GroupValueResponse, GroupValueWrite logger = logging.getLogger("xknx.log") @@ -112,17 +113,37 @@ async def process(self, telegram, always_callback=False): """Process incoming or outgoing telegram.""" if not self.has_group_address(telegram.destination_address): return False - if not self.payload_valid(telegram.payload): + if not isinstance( + telegram.payload, + ( + GroupValueWrite, + GroupValueResponse, + ), + ): + raise CouldNotParseTelegram( + "payload not a GroupValueWrite or GroupValueResponse", + payload=telegram.payload, + destination_address=telegram.destination_address, + source_address=telegram.source_address, + device_name=self.device_name, + feature_name=self.feature_name, + ) + if not self.payload_valid(telegram.payload.value): raise CouldNotParseTelegram( "payload invalid", payload=telegram.payload, - address=telegram.destination_address, + destination_address=telegram.destination_address, + source_address=telegram.source_address, device_name=self.device_name, feature_name=self.feature_name, ) self.xknx.state_updater.update_received(self) - if self.payload is None or always_callback or self.payload != telegram.payload: - self.payload = telegram.payload + if ( + self.payload is None + or always_callback + or self.payload != telegram.payload.value + ): + self.payload = telegram.payload.value if self.after_update_cb is not None: await self.after_update_cb() return True @@ -138,10 +159,9 @@ async def _send(self, payload, response=False): """Send payload as telegram to KNX bus.""" telegram = Telegram( destination_address=self.group_address, - telegramtype=( - TelegramType.GROUP_RESPONSE if response else TelegramType.GROUP_WRITE + payload=( + GroupValueResponse(payload) if response else GroupValueWrite(payload) ), - payload=payload, ) await self.xknx.telegrams.put(telegram) diff --git a/xknx/telegram/__init__.py b/xknx/telegram/__init__.py index b47b5fd09..1b222e1d5 100644 --- a/xknx/telegram/__init__.py +++ b/xknx/telegram/__init__.py @@ -8,4 +8,4 @@ # flake8: noqa from .address import GroupAddress, GroupAddressType, IndividualAddress from .address_filter import AddressFilter -from .telegram import Telegram, TelegramDirection, TelegramType +from .telegram import Telegram, TelegramDirection diff --git a/xknx/telegram/apci.py b/xknx/telegram/apci.py new file mode 100644 index 000000000..c5075ba2c --- /dev/null +++ b/xknx/telegram/apci.py @@ -0,0 +1,207 @@ +""" +Module for serialization and deserialization of APCI payloads. + +APCI stands for Application Layer Protocol Control Information. + +An APCI payload contains a service and payload. For example, a GroupValueWrite +is a service that takes a DPT as a value. +""" +from abc import ABC, abstractmethod +from enum import Enum +from typing import ClassVar, Optional, Union, cast + +from xknx.dpt import DPTArray, DPTBinary +from xknx.exceptions import ConversionError + + +def encode_cmd_and_payload( + cmd: "APCICommand", + encoded_payload: int = 0, + appended_payload: Optional[bytes] = None, +) -> bytes: + """Encode cmd and payload.""" + if appended_payload is None: + appended_payload = bytes() + + data = bytearray( + [ + (cmd.value >> 8) & 0xFF, + (cmd.value & 0xFF) | (encoded_payload & DPTBinary.APCI_BITMASK), + ] + ) + data.extend(appended_payload) + return data + + +class APCICommand(Enum): + """Enum class for APCI services.""" + + GROUP_READ = 0x0000 + GROUP_RESPONSE = 0x0040 + GROUP_WRITE = 0x0080 + + ESCAPE = 0x03C0 + + +class APCIExtendedCommand(Enum): + """Enum class for extended APCI services.""" + + +class APCI(ABC): + """ + Base class for ACPI services. + + This base class is only the interface for the derived classes. + """ + + code: ClassVar[APCICommand] = cast(APCICommand, None) + + @abstractmethod + def calculated_length(self) -> int: + """Get length of APCI payload - to be implemented in derived class.""" + + @abstractmethod + def from_knx(self, raw: bytes) -> None: + """Parse/deserialize from KNX/IP raw data - to be implemented in derived class.""" + + @abstractmethod + def to_knx(self) -> bytes: + """Serialize to KNX/IP raw data - to be implemented in derived class.""" + + def __eq__(self, other: object) -> bool: + """Equal operator.""" + return self.__dict__ == other.__dict__ + + @staticmethod + def resolve_apci(apci: int) -> "APCI": + """Return APCI instance from APCI command.""" + extended = (apci & APCICommand.ESCAPE.value) == APCICommand.ESCAPE.value + + if extended: + raise ConversionError( + f"Class not implemented for extended APCI {apci:#012b}." + ) + + apci = apci & 0x03C0 + + if apci == APCICommand.GROUP_READ.value: + return GroupValueRead() + if apci == APCICommand.GROUP_WRITE.value: + return GroupValueWrite() + if apci == APCICommand.GROUP_RESPONSE.value: + return GroupValueResponse() + raise ConversionError(f"Class not implemented for APCI {apci:#012b}.") + + +class GroupValueRead(APCI): + """ + GroupValueRead service. + + Does not have any payload. + """ + + code = APCICommand.GROUP_READ + + def calculated_length(self) -> int: + """Get length of APCI payload.""" + return 1 + + def from_knx(self, raw: bytes) -> None: + """Parse/deserialize from KNX/IP raw data.""" + + # Nothing to parse, but must be implemented explicitly. + return + + def to_knx(self) -> bytes: + """Serialize to KNX/IP raw data.""" + return encode_cmd_and_payload(self.code) + + def __str__(self) -> str: + """Return object as readable string.""" + return "" + + +class GroupValueWrite(APCI): + """ + GroupValueRead service. + + Takes a value (DPT) as payload. + """ + + code = APCICommand.GROUP_WRITE + + def __init__(self, value: Optional[Union[DPTBinary, DPTArray]] = None) -> None: + """Initialize a new instance of GroupValueWrite.""" + self.value = value + + def calculated_length(self) -> int: + """Get length of APCI payload.""" + if isinstance(self.value, DPTBinary): + return 1 + if isinstance(self.value, DPTArray): + return 1 + len(self.value.value) + raise TypeError() + + def from_knx(self, raw: bytes) -> None: + """Parse/deserialize from KNX/IP raw data.""" + if len(raw) == 2: + self.value = DPTBinary(raw[1] & DPTBinary.APCI_BITMASK) + else: + self.value = DPTArray(raw[2:]) + + def to_knx(self) -> bytes: + """Serialize to KNX/IP raw data.""" + if isinstance(self.value, DPTBinary): + return encode_cmd_and_payload(self.code, encoded_payload=self.value.value) + if isinstance(self.value, DPTArray): + return encode_cmd_and_payload( + self.code, appended_payload=bytes(self.value.value) + ) + raise TypeError() + + def __str__(self) -> str: + """Return object as readable string.""" + return f'' + + +class GroupValueResponse(APCI): + """ + GroupValueResponse service. + + Takes a value (DPT) as payload. + """ + + code = APCICommand.GROUP_RESPONSE + + def __init__(self, value: Optional[Union[DPTBinary, DPTArray]] = None) -> None: + """Initialize a new instance of GroupValueResponse.""" + self.value = value + + def calculated_length(self) -> int: + """Get length of APCI payload.""" + if isinstance(self.value, DPTBinary): + return 1 + if isinstance(self.value, DPTArray): + return 1 + len(self.value.value) + raise TypeError() + + def from_knx(self, raw: bytes) -> None: + """Parse/deserialize from KNX/IP raw data.""" + if len(raw) == 2: + self.value = DPTBinary(raw[1] & DPTBinary.APCI_BITMASK) + else: + self.value = DPTArray(raw[2:]) + + def to_knx(self) -> bytes: + """Serialize to KNX/IP raw data.""" + if isinstance(self.value, DPTBinary): + return encode_cmd_and_payload(self.code, encoded_payload=self.value.value) + if isinstance(self.value, DPTArray): + return encode_cmd_and_payload( + self.code, appended_payload=bytes(self.value.value) + ) + raise TypeError() + + def __str__(self) -> str: + """Return object as readable string.""" + return f'' diff --git a/xknx/telegram/telegram.py b/xknx/telegram/telegram.py index 759bc6e0a..3b4280c29 100644 --- a/xknx/telegram/telegram.py +++ b/xknx/telegram/telegram.py @@ -8,10 +8,9 @@ It contains -* the telegram type (e.g. GROUP_WRITE) * the direction (incoming or outgoing) * the group address (e.g. 1/2/3) -* and the payload (e.g. "12%" or "23.23 C". +* and the payload (e.g. GroupValueWrite("12%")). """ from enum import Enum @@ -27,14 +26,6 @@ class TelegramDirection(Enum): OUTGOING = "Outgoing" -class TelegramType(Enum): - """Enum class for type of telegram.""" - - GROUP_READ = "GroupValueRead" - GROUP_WRITE = "GroupValueWrite" - GROUP_RESPONSE = "GroupValueResponse" - - class Telegram: """Class for KNX telegrams.""" @@ -45,14 +36,12 @@ def __init__( destination_address: Union[GroupAddress, IndividualAddress] = GroupAddress( None ), - telegramtype: TelegramType = TelegramType.GROUP_WRITE, direction: TelegramDirection = TelegramDirection.OUTGOING, payload: Any = None, source_address: IndividualAddress = IndividualAddress(None), ) -> None: """Initialize Telegram class.""" self.destination_address = destination_address - self.telegramtype = telegramtype self.direction = direction self.payload = payload self.source_address = source_address @@ -60,10 +49,9 @@ def __init__( def __str__(self) -> str: """Return object as readable string.""" return ( - ''.format( self.direction.value, - self.telegramtype.value, self.source_address, self.destination_address, self.payload,