Examples

Sending Commands

A list of commands that can be sent can be found in the Input Commands section of the protocol documentation. Additional information on the user-level commands available can be found in the Operation Summary of the installer manual.

import asyncio
import contextlib

from nessclient import Client


async def main() -> None:
    host = "127.0.0.1"
    port = 65432
    client = Client(host=host, port=port)
    # Run background loops so responses are received while awaiting
    keepalive_task = asyncio.create_task(client.keepalive())
    try:
        # Send arming command via library abstraction
        await client.arm_away("1234")
        # Send panic command via library abstraction
        await client.panic("1234")
        # Send disarm command via library abstraction
        await client.disarm("1234")
        # Send aux control command for output 2 via library abstraction
        await client.aux_output(2)
        # Send custom command
        # In this instance, we are sending a status update command to view
        # output status
        await client.send_command("S15")
        # Send and await a status request to receive the decoded response
        # note: the background keepalive must be running for this to work
        resp = await client.send_command_and_wait("S14", timeout=5.0)
        print("Arming status response:", resp)
    finally:
        await client.close()
        keepalive_task.cancel()
        with contextlib.suppress(asyncio.CancelledError):
            await keepalive_task


if __name__ == "__main__":
    asyncio.run(main())

Listening for Events

import asyncio

from nessclient import Client, ArmingState, ArmingMode, BaseEvent


async def main() -> None:
    host = "127.0.0.1"
    port = 65432
    client = Client(host=host, port=port)

    @client.on_zone_change
    def on_zone_change(zone: int, triggered: bool) -> None:
        print("Zone {} changed to {}".format(zone, triggered))

    @client.on_state_change
    def on_state_change(state: ArmingState, _arming_mode: ArmingMode | None) -> None:
        print("Alarm state changed to {}".format(state))

    @client.on_event_received
    def on_event_received(event: BaseEvent) -> None:
        print("Event received:", event)

    try:
        await asyncio.gather(
            client.keepalive(),
            client.update(),
        )
    finally:
        client.close()


if __name__ == "__main__":
    asyncio.run(main())

Streaming Events

The nessclient API also exposes asynchronous streams as an alternative to callbacks. Instead of registering handlers, you can iterate over the following async generators:

  • client.stream_events()

  • client.stream_state_changes()

  • client.stream_zone_changes()

  • client.stream_aux_output_changes()

This approach fits naturally into asyncio applications and allows cooperative scheduling via await.

import asyncio

from nessclient import Client


async def main() -> None:
    client = Client(host="127.0.0.1", port=65432)
    events = client.stream_events()
    state_changes = client.stream_state_changes()
    zone_changes = client.stream_zone_changes()
    output_changes = client.stream_aux_output_changes()

    async def print_events() -> None:
        async for event in events:
            print("Event received:", event)

    async def print_state_changes() -> None:
        async for state, mode in state_changes:
            print("State changed:", state, mode)

    async def print_zone_changes() -> None:
        async for zone_id, triggered in zone_changes:
            print("Zone {} changed to {}".format(zone_id, triggered))

    async def print_output_changes() -> None:
        async for output_id, active in output_changes:
            print(f"Output {output_id} changed to {active}")

    try:
        await asyncio.gather(
            print_events(),
            print_state_changes(),
            print_zone_changes(),
            print_output_changes(),
            # Start keepalive, then trigger a one-off update for initial state.
            client.keepalive(),
            client.update(),
        )
    finally:
        await events.aclose()
        await state_changes.aclose()
        await zone_changes.aclose()
        await output_changes.aclose()
        await client.close()


if __name__ == "__main__":
    asyncio.run(main())