async def test_send_command_schema(client_session, url, ws_client,
                                   driver_ready, driver):
    """Test sending unsupported command."""
    client = Client(url, client_session)
    await client.connect()
    assert client.connected
    client.driver = driver
    await client.listen(driver_ready)
    ws_client.receive.assert_awaited()

    # test schema version is at server maximum
    if client.version.max_schema_version < MAX_SERVER_SCHEMA_VERSION:
        assert client.schema_version == client.version.max_schema_version

    # send command of current schema version should not fail
    with pytest.raises(NotConnected):
        await client.async_send_command({"command": "test"},
                                        require_schema=client.schema_version)
    # send command of unsupported schema version should fail
    with pytest.raises(InvalidServerVersion):
        await client.async_send_command({"command": "test"},
                                        require_schema=client.schema_version +
                                        2)
    with pytest.raises(InvalidServerVersion):
        await client.async_send_command_no_wait(
            {"command": "test"}, require_schema=client.schema_version + 2)
Exemple #2
0
async def client_fixture(loop, client_session, ws_client, uuid4):
    """Return a client with a mock websocket transport.

    This fixture needs to be a coroutine function to get an event loop
    when creating the client.
    """
    client = Client("ws://test.org", client_session)
    client._client = ws_client
    return client
Exemple #3
0
async def client_fixture(loop, client_session, ws_client, uuid4):
    """Return a client with a mock websocket transport.

    This fixture needs to be a coroutine function to get an event loop
    when creating the client.
    """
    client_session.ws_connect.side_effect = AsyncMock(return_value=ws_client)
    client = Client("ws://test.org", client_session)
    client.state = STATE_CONNECTED
    client.client = ws_client
    return client
async def test_listen_unknown_result_type(client_session, url, ws_client,
                                          result, driver_ready, driver):
    """Test websocket message with unknown type on listen."""
    client = Client(url, client_session)
    await client.connect()

    assert client.connected

    # Make sure there's a driver so we can test an unknown event.
    client.driver = driver
    result["type"] = "unknown"

    # Receiving an unknown message type should not error.
    await client.listen(driver_ready)

    ws_client.receive.assert_awaited()
async def test_listen_event(client_session, url, ws_client, messages,
                            ws_message, result, driver_ready):
    """Test receiving event result type on listen."""
    client = Client(url, client_session)
    await client.connect()

    assert client.connected

    result["type"] = "event"
    result["event"] = {
        "source": "node",
        "event": "value updated",
        "nodeId": 52,
        "args": {
            "commandClassName": "Basic",
            "commandClass": 32,
            "endpoint": 0,
            "property": "currentValue",
            "newValue": 255,
            "prevValue": 255,
            "propertyName": "currentValue",
        },
    }
    messages.append(ws_message)

    await client.listen(driver_ready)
    ws_client.receive.assert_awaited()
async def test_listen_without_connect(client_session, url, driver_ready):
    """Test listen without first being connected."""
    client = Client(url, client_session)
    assert not client.connected

    with pytest.raises(InvalidState):
        await client.listen(driver_ready)
async def test_listen(client_session, url, driver_ready):
    """Test client listen."""
    client = Client(url, client_session)

    assert not client.driver

    await client.connect()

    assert client.connected

    asyncio.create_task(client.listen(driver_ready))
    await driver_ready.wait()
    assert client.driver

    await client.disconnect()
    assert not client.connected
async def test_send_json_when_disconnected(client_session, url):
    """Test send json message when disconnected."""
    client = Client(url, client_session)

    assert not client.connected

    with pytest.raises(NotConnected):
        await client.async_send_command({"test": None})
async def test_max_schema_version(client_session, url, version_data):
    """Test client connect with invalid schema version."""
    version_data["maxSchemaVersion"] = 0
    client = Client(url, client_session)

    with pytest.raises(InvalidServerVersion):
        await client.connect()

    assert not client.connected
async def test_cannot_connect(client_session, url, error):
    """Test cannot connect."""
    client_session.ws_connect.side_effect = error
    client = Client(url, client_session)

    with pytest.raises(CannotConnect):
        await client.connect()

    assert not client.connected
async def test_listen_not_success(client_session, url, result, driver_ready):
    """Test receive result message with success False on listen."""
    result["success"] = False
    result["errorCode"] = "error_code"
    client = Client(url, client_session)
    await client.connect()

    with pytest.raises(FailedCommand):
        await client.listen(driver_ready)

    assert not client.connected
async def test_listen_invalid_message_data(client_session, url, messages,
                                           ws_message, driver_ready):
    """Test websocket message data that should raise on listen."""
    client = Client(url, client_session)
    await client.connect()
    assert client.connected

    ws_message.json.side_effect = ValueError("Boom")
    messages.append(ws_message)

    with pytest.raises(InvalidMessage):
        await client.listen(driver_ready)
Exemple #13
0
async def test_get_log_config_not_success(
    client_session, url, get_log_config_data, driver_ready
):
    """Test receive log config message with success False on listen."""
    get_log_config_data["success"] = False
    get_log_config_data["errorCode"] = "error_code"
    client = Client(url, client_session)
    await client.connect()

    with pytest.raises(FailedCommand):
        await client.listen(driver_ready)

    assert not client.connected
async def test_listen_error_message_types(client_session, url, messages,
                                          ws_message, message_type, exception,
                                          driver_ready):
    """Test different websocket message types that should raise on listen."""
    client = Client(url, client_session)
    await client.connect()
    assert client.connected

    ws_message.type = message_type
    messages.append(ws_message)

    with pytest.raises(exception):
        await client.listen(driver_ready)
async def test_listen_disconnect_message_types(client_session, url, ws_client,
                                               messages, ws_message,
                                               message_type, driver_ready):
    """Test different websocket message types that stop listen."""
    async with Client(url, client_session) as client:
        assert client.connected
        ws_message.type = message_type
        messages.append(ws_message)

        # This should break out of the listen loop before handling the received message.
        # Otherwise there will be an error.
        await client.listen(driver_ready)

    # Assert that we received a message.
    ws_client.receive.assert_awaited()
async def test_listen_client_error(client_session, url, ws_client, messages,
                                   ws_message, driver_ready):
    """Test websocket error on listen."""
    client = Client(url, client_session)
    await client.connect()
    assert client.connected

    messages.append(ws_message)

    ws_client.receive.side_effect = asyncio.CancelledError()

    # This should break out of the listen loop before any message is received.
    with pytest.raises(asyncio.CancelledError):
        await client.listen(driver_ready)

    assert not ws_message.json.called
async def test_connect_disconnect(client_session, url):
    """Test client connect and disconnect."""
    async with Client(url, client_session) as client:
        assert client.connected

    assert not client.connected
Exemple #18
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Z-Wave JS from a config entry."""
    client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass))
    initialized = asyncio.Event()
    dev_reg = await device_registry.async_get_registry(hass)

    async def async_on_connect() -> None:
        """Handle websocket is (re)connected."""
        LOGGER.info("Connected to Zwave JS Server")
        if initialized.is_set():
            # update entity availability
            async_dispatcher_send(hass, f"{DOMAIN}_connection_state")

    async def async_on_disconnect() -> None:
        """Handle websocket is disconnected."""
        LOGGER.info("Disconnected from Zwave JS Server")
        async_dispatcher_send(hass, f"{DOMAIN}_connection_state")

    async def async_on_initialized() -> None:
        """Handle initial full state received."""
        LOGGER.info("Connection to Zwave JS Server initialized.")
        initialized.set()

    @callback
    def async_on_node_ready(node: ZwaveNode) -> None:
        """Handle node ready event."""
        LOGGER.debug("Processing node %s", node)

        # register (or update) node in device registry
        register_node_in_dev_reg(hass, entry, dev_reg, client, node)

        # run discovery on all node values and create/update entities
        for disc_info in async_discover_values(node):
            LOGGER.debug("Discovered entity: %s", disc_info)
            async_dispatcher_send(hass, f"{DOMAIN}_add_{disc_info.platform}", disc_info)

    @callback
    def async_on_node_added(node: ZwaveNode) -> None:
        """Handle node added event."""
        # we only want to run discovery when the node has reached ready state,
        # otherwise we'll have all kinds of missing info issues.
        if node.ready:
            async_on_node_ready(node)
            return
        # if node is not yet ready, register one-time callback for ready state
        LOGGER.debug("Node added: %s - waiting for it to become ready.", node.node_id)
        node.once(
            "ready",
            lambda event: async_on_node_ready(event["node"]),
        )
        # we do submit the node to device registry so user has
        # some visual feedback that something is (in the process of) being added
        register_node_in_dev_reg(hass, entry, dev_reg, client, node)

    async def handle_ha_shutdown(event: Event) -> None:
        """Handle HA shutdown."""
        await client.disconnect()

    # register main event callbacks.
    unsubs = [
        client.register_on_initialized(async_on_initialized),
        client.register_on_disconnect(async_on_disconnect),
        client.register_on_connect(async_on_connect),
        hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, handle_ha_shutdown),
    ]

    # connect and throw error if connection failed
    asyncio.create_task(client.connect())
    try:
        async with timeout(CONNECT_TIMEOUT):
            await initialized.wait()
    except asyncio.TimeoutError as err:
        for unsub in unsubs:
            unsub()
        await client.disconnect()
        raise ConfigEntryNotReady from err

    hass.data[DOMAIN][entry.entry_id] = {
        DATA_CLIENT: client,
        DATA_UNSUBSCRIBE: unsubs,
    }

    # Set up websocket API
    async_register_api(hass)

    async def start_platforms() -> None:
        """Start platforms and perform discovery."""
        # wait until all required platforms are ready
        await asyncio.gather(
            *[
                hass.config_entries.async_forward_entry_setup(entry, component)
                for component in PLATFORMS
            ]
        )

        # run discovery on all ready nodes
        for node in client.driver.controller.nodes.values():
            async_on_node_added(node)

        # listen for new nodes being added to the mesh
        client.driver.controller.on(
            "node added", lambda event: async_on_node_added(event["node"])
        )

    hass.async_create_task(start_platforms())

    return True