async def test_put_dimvalue_list_cancel(generate_sanic_request, mock_velbus, module_address, channel): mock_velbus.set_expected_conversation([ VMB4DC_module_info_exchange(module_address), (VelbusFrame(address=module_address, message=SetDimvalue( channel=channel, dimvalue=100, dimspeed=0, )).to_bytes(), VelbusFrame(address=module_address, message=DimmercontrollerStatus( channel=channel, dimvalue=100, )).to_bytes()), ]) sanic_req = generate_sanic_request(method='PUT', body=json.dumps([{ "dimvalue": 100 }, { "dimvalue": 20, "when": "2000-01-01 00:00:00.1" }])) resp = await HttpApi.module_req(sanic_req, f'{module_address:02x}', f"/{channel}/e_dimvalue") assert 202 == resp.status await asyncio.sleep(0.05) mock_velbus.assert_conversation_happened_exactly() # Now interrupt this sleep with a new HTTP-call sanic_req = generate_sanic_request(method='PUT', body='42') mock_velbus.set_expected_conversation([ (VelbusFrame( address=module_address, message=SetDimvalue( channel=channel, dimvalue=42, dimspeed=0, ), ).to_bytes(), VelbusFrame(address=module_address, message=DimmercontrollerStatus( channel=channel, dimvalue=42, )).to_bytes()), ]) resp = await HttpApi.module_req(sanic_req, f'{module_address:02x}', f"/{channel}/e_dimvalue") assert resp.status // 100 == 2 await asyncio.sleep(0.1) # until 0.15 mock_velbus.assert_conversation_happened_exactly() assert len(HttpApi.modules[module_address].result().submodules[channel]. delayed_calls) == 0
async def test_put_dimvalue_int(generate_sanic_request, mock_velbus, module_address, channel): mock_velbus.set_expected_conversation([ VMB4DC_module_info_exchange(module_address), (VelbusFrame( address=module_address, message=SetDimvalue( channel=channel, dimvalue=100, dimspeed=0, ), ).to_bytes(), VelbusFrame( address=module_address, message=DimmercontrollerStatus( channel=channel, dimvalue=100, ), ).to_bytes()), ]) sanic_req = generate_sanic_request(method='PUT', body='100') resp = await HttpApi.module_req(sanic_req, f'{module_address:02x}', f"/{channel}/dimvalue") assert resp.status // 100 == 2 mock_velbus.assert_conversation_happened_exactly()
async def test_put_relay(generate_sanic_request, mock_velbus, module_address, channel, true_false): mock_velbus.set_expected_conversation([ VMB4RYNO_module_info_exchange(module_address), (VelbusFrame( address=module_address, message=SwitchRelay( command=SwitchRelay.Command.SwitchRelayOn if true_false else SwitchRelay.Command.SwitchRelayOff, channel=Index(8)(channel), ), ).to_bytes(), VelbusFrame( address=module_address, message=RelayStatus( channel=channel, relay_status=RelayStatus.RelayStatus.On if true_false else RelayStatus.RelayStatus.Off, ), ).to_bytes()) ]) sanic_req = generate_sanic_request(method='PUT', body=json.dumps(true_false)) resp = await HttpApi.module_req(sanic_req, f"{module_address:02x}", f"/{channel}/relay") assert resp.status == 200 assert resp.body.decode('utf-8') == json.dumps(true_false) await asyncio.sleep(0.05) # allow time to process the queue assert mock_velbus.assert_conversation_happened_exactly()
async def test_VMB1TS_message(generate_sanic_request, module_address, mock_velbus): mock_velbus.set_expected_conversation([ VMB1TS_module_info_exchange(module_address), ]) req = generate_sanic_request() await module_req(req, f'{module_address:02x}', '/type') mock_velbus.assert_conversation_happened_exactly() message( VelbusFrame( address=module_address, message=PushButtonStatus(just_pressed=Bitmap( 8)([True, False, False, False, False, False, False, False])), )) resp = await module_req(req, f'{module_address:02x}', '/heater') assert resp.status == 200 assert resp.body.decode('utf-8') == "true" message( VelbusFrame( address=module_address, message=PushButtonStatus(just_released=Bitmap( 8)([True, False, False, False, False, False, False, False])), )) resp = await module_req(req, f'{module_address:02x}', '/heater') assert resp.status == 200 assert resp.body.decode('utf-8') == "false"
async def test_put_relay_timer(generate_sanic_request, mock_velbus, module_address, channel): timer = 42 mock_velbus.set_expected_conversation([ VMB4RYNO_module_info_exchange(module_address), (VelbusFrame( address=module_address, message=StartRelayTimer( channel=channel, delay_time=timer, ), ).to_bytes(), VelbusFrame( address=module_address, message=RelayStatus( channel=channel, relay_status=RelayStatus.RelayStatus.On, ), ).to_bytes()) ]) sanic_req = generate_sanic_request(method='PUT', body=str(timer)) resp = await HttpApi.module_req(sanic_req, f"{module_address:02x}", f"/{channel}/relay") assert resp.status == 200 assert resp.body.decode('utf-8') == 'true' await asyncio.sleep(0.05) # allow time to process the queue assert mock_velbus.assert_conversation_happened_exactly()
async def test_ws(generate_sanic_request, mock_velbus): module_address = 0x11 # TODO: check for "all" addresses channel = 4 # TODO: check all channels mock_velbus.set_expected_conversation([ VMB4RYNO_module_info_exchange(module_address), (VelbusFrame( address=module_address, message=ModuleStatusRequest(channel=Index(8)(channel).to_int(), ), ).to_bytes(), VelbusFrame( address=module_address, message=RelayStatus( channel=channel, relay_status=RelayStatus.RelayStatus.Off, ), ).to_bytes()) ]) # Initialize the module sanic_req = generate_sanic_request() resp = await HttpApi.module_req(sanic_req, f"{module_address:02x}", f"/{channel}/relay") ws = Mock() ws.subscribed_modules = {module_address} ws.send = Mock(return_value=make_awaitable(None)) HttpApi.ws_clients.add(ws) sanic_req = generate_sanic_request() await HttpApi.ws_client_listen_module(VelbusHttpProtocol(sanic_req), module_address, ws) ws.send.assert_called_once_with('[{"op": "add", "path": "/11", "value": {' '"1": {}, ' '"2": {}, ' '"3": {}, ' '"4": {"relay": false}, ' '"5": {}' '}}]') ws.send.reset_mock() HttpApi.message( VelbusFrame(address=module_address, message=RelayStatus( channel=channel, relay_status=RelayStatus.RelayStatus.On, ))) ws.send.assert_called_once_with( '[{"op": "add", "path": "/11/4/relay", "value": true}]') ws.send.reset_mock() HttpApi.message( VelbusFrame(address=module_address, message=RelayStatus( channel=channel, relay_status=RelayStatus.RelayStatus.Off, ))) ws.send.assert_called_once_with( '[{"op": "add", "path": "/11/4/relay", "value": false}]') ws.send.reset_mock()
def VMB4RYNO_module_info_exchange(module_address): return (VelbusFrame( address=module_address, message=ModuleTypeRequest(), ).to_bytes(), VelbusFrame( address=module_address, message=ModuleType(module_info=VMB4RYNO_mi(), ), ).to_bytes())
def velbus_query(self, question: VelbusFrame, response_type: type, response_address: int = None, timeout: int = 2, additional_check=(lambda vbm: True)): assert question == VelbusFrame(address=1, message=ModuleTypeRequest()) return make_awaitable( VelbusFrame(address=1, message=ModuleType(module_info=VMB4RYNO_mi())) )
async def test_ws(generate_sanic_request, mock_velbus, module_address, channel): mock_velbus.set_expected_conversation([ VMB2BLE_module_info_exchange(module_address), VMB2BLE_module_status_exchange(module_address, channel, 7), ]) # Initialize the module sanic_req = generate_sanic_request() resp = await module_req(sanic_req, f"{module_address:02x}", f"/{channel}/position") assert 200 == resp.status client_state = dict() def receive(ops: str): patch = jsonpatch.JsonPatch(json.loads(ops)) nonlocal client_state client_state = patch.apply(client_state) return make_awaitable(None) ws = Mock() ws.subscribed_modules = {module_address} ws.send = receive HttpApi.ws_clients.add(ws) sanic_req = generate_sanic_request() await HttpApi.ws_client_listen_module(VelbusHttpProtocol(sanic_req), module_address, ws) assert "off" == client_state[f"{module_address:02x}"][str( channel)]["status"] assert 7 == client_state[f"{module_address:02x}"][str(channel)]["position"] HttpApi.message( VelbusFrame(address=module_address, message=BlindStatusV2( channel=channel, blind_status=BlindStatusV2.BlindStatus.Up, blind_position=7, ))) assert "up" == client_state[f"{module_address:02x}"][str( channel)]["status"] HttpApi.message( VelbusFrame(address=module_address, message=BlindStatusV2( channel=channel, blind_status=BlindStatusV2.BlindStatus.Off, blind_position=0, ))) assert "off" == client_state[f"{module_address:02x}"][str( channel)]["status"] assert 0 == client_state[f"{module_address:02x}"][str(channel)]["position"]
def test_decode(): b = b'\x0f\xfb\x01\x40\xb5\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a == VelbusFrame(address=1, message=ModuleTypeRequest()) assert json.loads(json.dumps(a.message.to_json_able())) == { 'type': 'ModuleTypeRequest', 'properties': {} }
def VMB2BLE_module_status_exchange(module_address, channel, position): return (VelbusFrame(address=module_address, message=ModuleStatusRequest( channel=channel, )).to_bytes(), VelbusFrame( address=module_address, message=BlindStatusV2( channel=channel, blind_status=BlindStatusV2.BlindStatus.Off, blind_position=position, ), ).to_bytes())
def test_data_8pbu(): b = b'\x0f\xfb\x00\x07\xed\x01\x02\x04\x08\x10\xaa\x39\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == ModuleStatus8PBU( channel_pressed=[ False, False, False, False, False, False, False, True ], channel_enabled=[ False, False, False, False, False, False, True, False ], channel_not_inverted=[ False, False, False, False, False, True, False, False ], channel_locked=[False, False, False, False, True, False, False, False], channel_program_disabled=[ False, False, False, True, False, False, False, False ], prog_sunset_enabled=True, prog_sunrise_enabled=False, alarm2=ModuleStatus8PBU.LocalGlobal.Global, alarm2_enabled=False, alarm1=ModuleStatus8PBU.LocalGlobal.Global, alarm1_enabled=False, program=ModuleStatus8PBU.Program.Winter, ) assert json.dumps(a.to_json_able())
def test_decode_v1(): b = b'\x0f\xfb\x2b\x08\xec\x0c\x01\x00\x00\x00\x00\x00\xca\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == BlindStatusV1( channel=2, default_timeout=BlindTimeout.t30sec, led_status=BlindStatusV1.LedStatus.Off, ) assert json.loads(json.dumps(a.message.to_json_able())) == { 'type': 'BlindStatusV1', 'properties': { 'channel': 2, 'blind_status': { 'name': 'Off', 'value': 0 }, 'default_timeout': { 'name': 't30sec', 'value': 1 }, 'led_status': { 'name': 'Off', 'value': 0 }, 'delay_time': 0, } }
def test_decode_too_long(): b = b'\x0f\xf8\x00\x02\x0a\x00\xed\x04' # BusActive, but too long a = VelbusFrame.from_bytes(b) assert a.message == UnknownMessage(priority=0, data=b'\x0a\x00') assert a.to_bytes() == b assert json.dumps(a.to_json_able())
def test_pressed(): b = b'\x0f\xf8\x00\x04\x00\x01\x0a\x00\xea\x04' a = VelbusFrame.from_bytes(b) assert a.message == PushButtonStatus( just_pressed=[False, False, False, False, False, False, False, True], just_released=[False, False, False, False, True, False, True, False], ) assert a.to_bytes() == b
def test_decode_VMBGPOD(): b = b'\x0f\xfb\x00\x07\xff\x28\x00\x00\x00\x00\x00\xc8\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == ModuleType(module_info=VMBGPOD()) assert json.dumps(a.to_json_able())
def test_decode_too_short(): b = b'\x0f\xf8\x00\x07\xea\x00\x00\x00\x00\x00\x00\x08\x04' # SensorTemperature, but too short a = VelbusFrame.from_bytes(b) assert a.message == UnknownMessage(priority=0, data=b'\xea\x00\x00\x00\x00\x00\x00') assert a.to_bytes() == b assert json.dumps(a.to_json_able())
def test_decode(): b = b'\x0f\xf8\x00\x04\x00\x00\x00\x00\xf5\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == PushButtonStatus() assert json.dumps(a.to_json_able())
def test_decode_8pbu(): b = b'\x0f\xfb\x00\x07\xed\x00\x00\x00\x00\x00\x00\x02\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == ModuleStatus8PBU() assert json.dumps(a.to_json_able())
def test_decode(): b = b'\x0f\xfb\x00\x08\xea\x00\x00\x00\x00\x00\x00\x00\x04\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == TemperatureSensorStatus() assert json.dumps(a.to_json_able())
def test_decode(): b = b'\x0f\xfb\x00\x02\xfa\x00\xfa\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == ModuleStatusRequest() assert json.dumps(a.to_json_able())
def test_decode_unknown(): b = b'\x0f\xfb\x73\x07\xff\xff\x8b\xa4\x01\x16\x12\x26\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == ModuleType(module_info=UnknownModuleInfo( data=b'\xff\x8b\xa4\x01\x16\x12')) assert json.dumps(a.to_json_able())
async def test_VMB2BL_instantiation(generate_sanic_request, module_address, mock_velbus): mock_velbus.set_expected_conversation([ (VelbusFrame( address=module_address, message=ModuleTypeRequest(), ).to_bytes(), VelbusFrame( address=module_address, message=ModuleType(module_info=VMB2BL_mi(), ), ).to_bytes()), ]) req = generate_sanic_request() resp = await module_req(req, f'{module_address:02x}', '/type') mock_velbus.assert_conversation_happened_exactly() assert 200 == resp.status assert f'VMB2BL at 0x{module_address:02x}\r\n' == resp.body.decode('utf-8')
def test_attributes(): b = b'\x0f\xfb\x00\x08\xea\x01\x00\x00\x00\x00\x00\x00\x03\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b s = TemperatureSensorStatus(mode_push_button_locked=True, ) assert a.message == s b = b'\x0f\xfb\x00\x08\xea\x00\x00\x00\x2b\x00\x00\x00\xd9\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b s = TemperatureSensorStatus(temperature=21.5, ) assert a.message == s b = b'\x0f\xfb\x00\x08\xea\x00\x00\x00\x00\x00\x01\x23\xe0\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b s = TemperatureSensorStatus(sleep_timer=0x123, ) assert a.message == s assert json.dumps(s.to_json_able())
async def test_reply(mock_velbus, module_address): mock_velbus.set_expected_conversation([ (VelbusFrame( address=module_address, message=ModuleTypeRequest(), ).to_bytes(), VelbusFrame( address=module_address, message=ModuleType(module_info=VMB4RYNO(), ), ).to_bytes()) ]) bus = VelbusProtocol(client_id="INTERNAL") await bus.velbus_query( VelbusFrame( address=module_address, message=ModuleTypeRequest(), ), ModuleType, )
async def test_VMB1TS_temperature(generate_sanic_request, module_address, mock_velbus): mock_velbus.set_expected_conversation([ VMB1TS_module_info_exchange(module_address), (VelbusFrame( address=module_address, message=SensorTemperatureRequest(), ).to_bytes(), VelbusFrame( address=module_address, message=SensorTemperature(current_temperature=22, ), ).to_bytes()), ]) req = generate_sanic_request() resp = await module_req(req, f'{module_address:02x}', '/temperature') mock_velbus.assert_conversation_happened_exactly() assert 200 == resp.status assert f'22.0' == resp.body.decode('utf-8')
def test_decode3(): b = b'\x0f\xfb\x00\x06\xf2\x01ABCD\xf3\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == SensorName3( command=SensorName3.Command.SensorName_part3, sensor_name=b'ABCD', ) assert json.dumps(a.message.to_json_able())
def test_decode(): b = b'\x0f\xf8\x00\x01\x0b\xed\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == RxBufFull() assert json.loads(json.dumps(a.message.to_json_able())) == { 'type': 'RxBufFull', 'properties': {} }
def test_decode(): b = b'\x0f\xf8\x00\x01\x0a\xee\x04' a = VelbusFrame.from_bytes(b) assert a.to_bytes() == b assert a.message == BusActive() assert json.loads(json.dumps(a.message.to_json_able())) == { 'type': 'BusActive', 'properties': {} }
async def test_VMB1TS_heater(generate_sanic_request, module_address, mock_velbus): mock_velbus.set_expected_conversation([ VMB1TS_module_info_exchange(module_address), (VelbusFrame( address=module_address, message=ModuleStatusRequest(), ).to_bytes(), VelbusFrame( address=module_address, message=TemperatureSensorStatus(heater=True, ), ).to_bytes()), ]) req = generate_sanic_request() resp = await module_req(req, f'{module_address:02x}', '/heater') mock_velbus.assert_conversation_happened_exactly() assert resp.status == 200 assert resp.body.decode('utf-8') == 'true'