def test_process(self): """Test process telegram with notification. Test if device was updated.""" xknx = XKNX(loop=self.loop) notification = Notification(xknx, 'Warning', group_address='1/2/3') telegram_set = Telegram(GroupAddress('1/2/3'), payload=DPTArray( DPTString().to_knx("Ein Prosit!"))) self.loop.run_until_complete( asyncio.Task(notification.process(telegram_set))) self.assertEqual(notification.message, "Ein Prosit!") telegram_unset = Telegram(GroupAddress('1/2/3'), payload=DPTArray(DPTString().to_knx(""))) self.loop.run_until_complete( asyncio.Task(notification.process(telegram_unset))) self.assertEqual(notification.message, "")
def test_process_wrong_payload(self): """Test process wrong telegram (wrong payload type).""" xknx = XKNX(loop=self.loop) binary_sensor = BinarySensor(xknx, 'Warning', group_address_state='1/2/3') telegram = Telegram(GroupAddress('1/2/3'), payload=DPTArray((0x1, 0x2, 0x3))) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(asyncio.Task(binary_sensor.process(telegram)))
def to_knx(self, value): """ Convert value (4-6 bytes) to payload (6 bytes). * Structure of DPT 251.600 ** Bytes 0, 1: *** Bit 0-11: 0 *** Bit 12,13,14,15: R,G,B,W value valid? ** Byte 2: R value ** Byte 3: G value ** Byte 4: B value ** Byte 5: W value In case we receive * > 6 bytes: error * 6 bytes: all bytes are passed through * 5 bytes: 0x00 left padding * 4 bytes: 0x000f left padding * < 4 bytes: error """ if not isinstance(value, (list, tuple)): raise ConversionError("Cannot serialize RemoteValueColorRGBW (wrong type, expecting list of 4-6 bytes))", value=value, type=type(value)) if len(value) < 4 or len(value) > 6: raise ConversionError("Cannot serialize value to DPT 251.600 (wrong length, expecting list of 4-6 bytes)", value=value, type=type(value)) rgbw = value[len(value)-4:] if any(not isinstance(color, int) for color in rgbw) \ or any(color < 0 for color in rgbw) \ or any(color > 255 for color in rgbw): raise ConversionError("Cannot serialize DPT 251.600 (wrong RGBW values)", value=value) return DPTArray([0x00, 0x0f][:6-len(value)] + list(value))
def test_tunnelling(self): """Test tunnelling from KNX bus.""" # pylint: disable=too-many-locals xknx = XKNX(loop=self.loop) communication_channel_id = 23 udp_client = UDPClient(xknx, ("192.168.1.1", 0), ("192.168.1.2", 1234)) telegram = Telegram(GroupAddress('1/2/3'), payload=DPTArray((0x1, 0x2, 0x3))) sequence_counter = 42 src_address = PhysicalAddress('2.2.2') tunnelling = Tunnelling(xknx, udp_client, telegram, src_address, sequence_counter, communication_channel_id) tunnelling.timeout_in_seconds = 0 self.assertEqual(tunnelling.awaited_response_class, TunnellingAck) self.assertEqual(tunnelling.communication_channel_id, communication_channel_id) # Expected KNX/IP-Frame: exp_knxipframe = KNXIPFrame(xknx) exp_knxipframe.init(KNXIPServiceType.TUNNELLING_REQUEST) exp_knxipframe.body.cemi.telegram = telegram exp_knxipframe.body.cemi.src_addr = src_address exp_knxipframe.body.communication_channel_id = communication_channel_id exp_knxipframe.body.sequence_counter = sequence_counter exp_knxipframe.normalize() print(exp_knxipframe) with patch('xknx.io.UDPClient.send') as mock_udp_send, \ patch('xknx.io.UDPClient.getsockname') as mock_udp_getsockname: mock_udp_getsockname.return_value = ("192.168.1.3", 4321) self.loop.run_until_complete(asyncio.Task(tunnelling.start())) mock_udp_send.assert_called_with(exp_knxipframe) # Response KNX/IP-Frame with wrong type wrong_knxipframe = KNXIPFrame(xknx) wrong_knxipframe.init(KNXIPServiceType.CONNECTIONSTATE_REQUEST) with patch('logging.Logger.warning') as mock_warning: tunnelling.response_rec_callback(wrong_knxipframe, None) mock_warning.assert_called_with('Cant understand knxipframe') # Response KNX/IP-Frame with error: err_knxipframe = KNXIPFrame(xknx) err_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK) err_knxipframe.body.status_code = ErrorCode.E_CONNECTION_ID with patch('logging.Logger.warning') as mock_warning: tunnelling.response_rec_callback(err_knxipframe, None) mock_warning.assert_called_with( "Error: KNX bus responded to request of type '%s' with error in '%s': %s", type(tunnelling).__name__, type(err_knxipframe.body).__name__, ErrorCode.E_CONNECTION_ID) # Correct Response KNX/IP-Frame: res_knxipframe = KNXIPFrame(xknx) res_knxipframe.init(KNXIPServiceType.TUNNELLING_ACK) with patch('logging.Logger.debug') as mock_debug: tunnelling.response_rec_callback(res_knxipframe, None) mock_debug.assert_called_with( 'Success: received correct answer from KNX bus: %s', ErrorCode.E_NO_ERROR) self.assertTrue(tunnelling.success)
def test_set_operation_mode_with_separate_addresses(self): """Test set_operation_mode with combined and separated group adddresses defined.""" xknx = XKNX(loop=self.loop) climate_mode = ClimateMode( xknx, 'TestClimate', group_address_operation_mode='1/2/4', group_address_operation_mode_protection='1/2/5', group_address_operation_mode_night='1/2/6', group_address_operation_mode_comfort='1/2/7') self.loop.run_until_complete( asyncio.Task( climate_mode.set_operation_mode(HVACOperationMode.COMFORT))) self.assertEqual(xknx.telegrams.qsize(), 4) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram, Telegram(GroupAddress('1/2/4'), payload=DPTArray(1))) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram, Telegram(GroupAddress('1/2/5'), payload=DPTBinary(0))) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram, Telegram(GroupAddress('1/2/6'), payload=DPTBinary(0))) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram, Telegram(GroupAddress('1/2/7'), payload=DPTBinary(1)))
def test_telegram_set(self): """Test parsing and streaming CEMIFrame KNX/IP packet with DPTArray/DPTTime as payload.""" xknx = XKNX(loop=self.loop) knxipframe = KNXIPFrame(xknx) knxipframe.init(KNXIPServiceType.ROUTING_INDICATION) knxipframe.body.src_addr = Address("1.2.2") telegram = Telegram() telegram.group_address = Address(337) telegram.payload = DPTArray(DPTTime().to_knx({ 'hours': 13, 'minutes': 23, 'seconds': 42 })) knxipframe.body.telegram = telegram knxipframe.body.set_hops(5) knxipframe.normalize() raw = ((0x06, 0x10, 0x05, 0x30, 0x00, 0x14, 0x29, 0x00, 0xbc, 0xd0, 0x12, 0x02, 0x01, 0x51, 0x04, 0x00, 0x80, 13, 23, 42)) self.assertEqual(knxipframe.header.to_knx(), list(raw[0:6])) self.assertEqual(knxipframe.body.to_knx(), list(raw[6:])) self.assertEqual(knxipframe.to_knx(), list(raw))
async def broadcast_time(self, response): """Broadcast time to KNX bus.""" if self.broadcast_type == DateTimeBroadcastType.DATETIME: broadcast_data = DPTDateTime.current_datetime_as_knx() await self.send(self.group_address, DPTArray(broadcast_data), response=response) elif self.broadcast_type == DateTimeBroadcastType.DATE: broadcast_data = DPTDate.current_date_as_knx() await self.send(self.group_address, DPTArray(broadcast_data), response=response) elif self.broadcast_type == DateTimeBroadcastType.TIME: broadcast_data = DPTTime.current_time_as_knx() await self.send(self.group_address, DPTArray(broadcast_data), response=response)
def test_process_fan_payload_invalid_length(self): """Test process wrong telegrams. (wrong payload length).""" # pylint: disable=invalid-name xknx = XKNX(loop=self.loop) fan = Fan(xknx, name="TestFan", group_address_speed='1/2/3') telegram = Telegram(GroupAddress('1/2/3'), payload=DPTArray((23, 24))) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(asyncio.Task(fan.process(telegram)))
def test_process_payload_invalid_length(self): """Test process wrong telegram (wrong payload length).""" # pylint: disable=invalid-name xknx = XKNX(loop=self.loop) notification = Notification(xknx, 'Warning', group_address='1/2/3') telegram = Telegram(GroupAddress('1/2/3'), payload=DPTArray((23, 24))) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(asyncio.Task(notification.process(telegram)))
def test_set(self): """Test setting value.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueSceneNumber( xknx, group_address=GroupAddress("1/2/3")) self.loop.run_until_complete(asyncio.Task(remote_value.set(10))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram(GroupAddress('1/2/3'), payload=DPTArray((0x0A, )))) self.loop.run_until_complete(asyncio.Task(remote_value.set(11))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram(GroupAddress('1/2/3'), payload=DPTArray((0x0B, ))))
def test_base_temperature(self): """Test base temperature.""" # pylint: disable=no-self-use xknx = XKNX(loop=self.loop) climate = Climate(xknx, 'TestClimate', group_address_target_temperature_state='1/2/1', group_address_target_temperature='1/2/2', group_address_setpoint_shift='1/2/3') self.loop.run_until_complete( asyncio.Task(climate.set_target_temperature(21.00))) self.assertEqual(xknx.telegrams.qsize(), 1) self.assertEqual( xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/2'), payload=DPTArray(DPT2ByteFloat().to_knx(21.00)))) self.assertFalse(climate.initialized_for_setpoint_shift_calculations) self.assertEqual(climate.base_temperature, None) # setpoint_shift initialized after target_temperature (no temperature change) self.loop.run_until_complete( asyncio.Task(climate.set_setpoint_shift(1))) self.assertEqual(xknx.telegrams.qsize(), 1) self.assertEqual( xknx.telegrams.get_nowait(), # DEFAULT_SETPOINT_SHIFT_STEP is 0.5 -> payload = setpoint_shift * 2 Telegram(GroupAddress('1/2/3'), payload=DPTArray(2))) self.assertTrue(climate.initialized_for_setpoint_shift_calculations) self.assertEqual(climate.base_temperature, 20.00) # setpoint_shift changed after initialisation self.loop.run_until_complete( asyncio.Task(climate.set_setpoint_shift(2))) # setpoint_shift and target_temperature are sent to the bus self.assertEqual(xknx.telegrams.qsize(), 2) self.assertEqual( xknx.telegrams.get_nowait(), # DEFAULT_SETPOINT_SHIFT_STEP is 0.5 -> payload = setpoint_shift * 2 Telegram(GroupAddress('1/2/3'), payload=DPTArray(4))) self.assertTrue(climate.initialized_for_setpoint_shift_calculations) self.assertEqual(climate.base_temperature, 20.00) self.assertEqual(climate.target_temperature.value, 22)
def test_set_speed(self): """Test setting the speed of a Fan.""" xknx = XKNX(loop=self.loop) fan = Fan(xknx, name="TestFan", group_address_speed='1/2/3') self.loop.run_until_complete(asyncio.Task(fan.set_speed(55))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() # 140 is 55% as byte (0...255) self.assertEqual( telegram, Telegram(GroupAddress('1/2/3'), payload=DPTArray(140)))
def test_process(self): """Test process telegram.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueDpt2ByteUnsigned( xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram(group_address=GroupAddress("1/2/3"), payload=DPTArray((0x0A, 0x0B))) self.loop.run_until_complete( asyncio.Task(remote_value.process(telegram))) self.assertEqual(remote_value.value, 2571)
def test_str_scaling(self): xknx = XKNX(self.loop, start=False) sensor = Sensor(xknx, 'TestSensor', group_address='1/2/3', value_type="percent") sensor.state = DPTArray((0x40, )) self.assertEqual(sensor.resolve_state(), "25") self.assertEqual(sensor.unit_of_measurement(), "%")
def test_set(self): """Test notificationing off notification.""" xknx = XKNX(loop=self.loop) notification = Notification(xknx, 'Warning', group_address='1/2/3') self.loop.run_until_complete(asyncio.Task(notification.set("Ein Prosit!"))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual(telegram, Telegram(GroupAddress('1/2/3'), payload=DPTArray(DPTString().to_knx("Ein Prosit!"))))
def test_process(self): """Test process telegram.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueColorRGB(xknx, group_address=GroupAddress("1/2/3")) telegram = Telegram(group_address=GroupAddress("1/2/3"), payload=DPTArray((0x64, 0x65, 0x66))) self.loop.run_until_complete( asyncio.Task(remote_value.process(telegram))) self.assertEqual(remote_value.value, (100, 101, 102))
def test_process_speed(self): """Test process / reading telegrams from telegram queue. Test if speed is processed.""" xknx = XKNX(loop=self.loop) fan = Fan(xknx, name="TestFan", group_address_speed='1/2/3') self.assertEqual(fan.current_speed, None) # 140 is 55% as byte (0...255) telegram = Telegram(GroupAddress('1/2/3'), payload=DPTArray(140)) self.loop.run_until_complete(asyncio.Task(fan.process(telegram))) self.assertEqual(fan.current_speed, 55)
def test_target_temperature_modified_step(self): """Test increase target temperature with modified step size.""" # pylint: disable=no-self-use xknx = XKNX(loop=self.loop) climate = Climate(xknx, 'TestClimate', group_address_target_temperature='1/2/2', group_address_setpoint_shift='1/2/3', setpoint_shift_step=0.1, setpoint_shift_max=10, setpoint_shift_min=-10) self.loop.run_until_complete( asyncio.Task(climate.set_setpoint_shift(3))) self.assertEqual(xknx.telegrams.qsize(), 1) self.assertEqual( xknx.telegrams.get_nowait(), # setpoint_shift_step is 0.1 -> payload = setpoint_shift * 10 Telegram(GroupAddress('1/2/3'), payload=DPTArray(30))) self.loop.run_until_complete( asyncio.Task(climate.target_temperature.set(23.00))) self.assertEqual(xknx.telegrams.qsize(), 1) self.assertEqual( xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/2'), payload=DPTArray(DPT2ByteFloat().to_knx(23.00)))) self.assertEqual(climate.base_temperature, 20.00) self.loop.run_until_complete( asyncio.Task(climate.set_target_temperature(24.00))) self.assertEqual(xknx.telegrams.qsize(), 2) self.assertEqual(xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/3'), payload=DPTArray(40))) self.assertEqual( xknx.telegrams.get_nowait(), Telegram(GroupAddress('1/2/2'), payload=DPTArray(DPT2ByteFloat().to_knx(24.00)))) self.assertEqual(climate.target_temperature.value, 24.00) # Test max/min target temperature self.assertEqual(climate.target_temperature_max, 30.00) self.assertEqual(climate.target_temperature_min, 10.00)
def test_str_humidity(self): """Test resolve state with humidity sensor.""" xknx = XKNX(loop=self.loop) sensor = Sensor(xknx, 'TestSensor', group_address='1/2/3', value_type="humidity") sensor.state = DPTArray((0x0e, 0x73)) self.assertEqual(sensor.resolve_state(), 33.02) self.assertEqual(sensor.unit_of_measurement(), "%")
def test_str_temp(self): """Test resolve state with temperature sensor.""" xknx = XKNX(loop=self.loop) sensor = Sensor(xknx, 'TestSensor', group_address='1/2/3', value_type="temperature") sensor.state = DPTArray((0x0c, 0x1a)) self.assertEqual(sensor.resolve_state(), 21.00) self.assertEqual(sensor.unit_of_measurement(), "°C")
def test_process_operation_mode_payload_invalid_length(self): """Test process wrong telegram for operation mode (wrong payload length).""" xknx = XKNX(loop=self.loop) climate = Climate(xknx, 'TestClimate', group_address_operation_mode='1/2/5', group_address_controller_status='1/2/3') telegram = Telegram(GroupAddress('1/2/5'), payload=DPTArray((23, 24))) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete( asyncio.Task(climate.process(telegram)))
def test_process_temperature(self): """Test process / reading telegrams from telegram queue. Test if temperature is processed correctly.""" xknx = XKNX(loop=self.loop) climate = Climate(xknx, 'TestClimate', group_address_temperature='1/2/3') telegram = Telegram(GroupAddress('1/2/3')) telegram.payload = DPTArray(DPTTemperature().to_knx(21.34)) self.loop.run_until_complete(asyncio.Task(climate.process(telegram))) self.assertEqual(climate.temperature.value, 21.34)
def test_process_operation_mode(self): """Test process / reading telegrams from telegram queue. Test if setpoint is processed correctly.""" xknx = XKNX(loop=self.loop) climate = Climate( xknx, 'TestClimate', group_address_operation_mode='1/2/5', group_address_controller_status='1/2/3') for operation_mode in HVACOperationMode: telegram = Telegram(Address('1/2/5')) telegram.payload = DPTArray(DPTHVACMode.to_knx(operation_mode)) self.loop.run_until_complete(asyncio.Task(climate.process(telegram))) self.assertEqual(climate.operation_mode, operation_mode) for operation_mode in HVACOperationMode: if operation_mode == HVACOperationMode.AUTO: continue telegram = Telegram(Address('1/2/3')) telegram.payload = DPTArray(DPTControllerStatus.to_knx(operation_mode)) self.loop.run_until_complete(asyncio.Task(climate.process(telegram))) self.assertEqual(climate.operation_mode, operation_mode)
def test_process_color(self): """Test process / reading telegrams from telegram queue. Test if color is processed.""" xknx = XKNX(loop=self.loop) light = Light(xknx, name="TestLight", group_address_switch='1/2/3', group_address_color='1/2/5') self.assertEqual(light.current_color, (None, None)) telegram = Telegram(GroupAddress('1/2/5'), payload=DPTArray((23, 24, 25))) self.loop.run_until_complete(asyncio.Task(light.process(telegram))) self.assertEqual(light.current_color, ((23, 24, 25), None))
def test_process_setpoint(self): xknx = XKNX(self.loop, start=False) thermostat = Thermostat(xknx, 'TestThermostat', group_address_setpoint='1/2/3') telegram = Telegram(Address('1/2/3')) telegram.payload = DPTArray(DPTTemperature().to_knx(21.34)) thermostat.process(telegram) self.assertEqual(thermostat.setpoint, 21.34)
def test_process_color_temperature_payload_invalid_length(self): """Test process wrong telegrams. (wrong payload length).""" # pylint: disable=invalid-name xknx = XKNX(loop=self.loop) light = Light(xknx, name="TestLight", group_address_switch='1/2/3', group_address_color_temperature='1/2/5') telegram = Telegram(GroupAddress('1/2/5'), payload=DPTArray((23))) with self.assertRaises(CouldNotParseTelegram): self.loop.run_until_complete(asyncio.Task(light.process(telegram)))
def test_str_percent(self): """Test resolve state with percent sensor.""" xknx = XKNX(loop=self.loop) expose_sensor = ExposeSensor(xknx, 'TestSensor', group_address='1/2/3', value_type="percent") expose_sensor.sensor_value.payload = DPTArray((0x40, )) self.assertEqual(expose_sensor.resolve_state(), 75) self.assertEqual(expose_sensor.unit_of_measurement(), "%")
def test_set(self): """Test setting value.""" xknx = XKNX(loop=self.loop) remote_value = RemoteValueDpt2ByteUnsigned( xknx, group_address=GroupAddress("1/2/3")) self.loop.run_until_complete(asyncio.Task(remote_value.set(2571))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram(GroupAddress('1/2/3'), payload=DPTArray((0x0A, 0x0B)))) self.loop.run_until_complete(asyncio.Task(remote_value.set(5500))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram(GroupAddress('1/2/3'), payload=DPTArray(( 0x15, 0x7C, ))))
def test_process_percent(self): """Test reading percent expose sensor from bus.""" xknx = XKNX(loop=self.loop) expose_sensor = ExposeSensor(xknx, 'TestSensor', value_type='percent', group_address='1/2/3') expose_sensor.sensor_value.payload = DPTArray((0x40, )) telegram = Telegram(GroupAddress('1/2/3')) telegram.telegramtype = TelegramType.GROUP_READ self.loop.run_until_complete( asyncio.Task(expose_sensor.process(telegram))) self.assertEqual(xknx.telegrams.qsize(), 1) telegram = xknx.telegrams.get_nowait() self.assertEqual( telegram, Telegram(GroupAddress('1/2/3'), TelegramType.GROUP_RESPONSE, payload=DPTArray((0x40, ))))
def test_str_electric_potential(self): """Test resolve state with voltage sensor.""" xknx = XKNX(loop=self.loop) sensor = Sensor(xknx, 'TestSensor', group_address_state='1/2/3', value_type="electric_potential") sensor.sensor_value.payload = DPTArray((0x43, 0x65, 0xE3, 0xD7)) self.assertEqual(round(sensor.resolve_state(), 2), 229.89) self.assertEqual(sensor.unit_of_measurement(), "V")