Skip to content

Understanding events

Version 0.11 introduces these event classes in place of the envelope API from 0.10. See the migration notes.

Backends deliver inventory and activity as frozen, slotted dataclasses. DeviceEvent is the union of these classes. Import them from lorawan_connection. Each class fixes its own type. Callers do not pass it to the constructor.

Every event exposes these fields:

Field Type Meaning
network_id str Application-owned identity of the logical network.
dev_eui str Device identifier, normalized to 16 lowercase hexadecimal characters.
received_at datetime Time of the event. Inventory events default to creation time in UTC. Activity events use the server receipt time.
type EventType Fixed by the event class.
descriptor DeviceDescriptor | None Inventory metadata, when available.

All constructor arguments are keyword-only. Inventory events take descriptor and an optional received_at. Their network_id and dev_eui are properties of the descriptor.

Activity events accept a descriptor or explicit network_id and dev_eui values. With a descriptor, identifiers are derived from it. Conflicting explicit values raise ValueError. Without one, both identifiers are required.

descriptor is required. received_at defaults to datetime.now(UTC) for each new event. Pass an explicit timestamp when replaying an earlier observation. The descriptor reference lists the device’s name, server application, profile, and catalog fields.

A device is available to the subscriber. Subscriptions also replay existing devices with this event.

Fixed type: EventType.ADDED ("added").

Field Type Meaning
descriptor DeviceDescriptor Current device metadata.
received_at datetime Event timestamp. Defaults to the current UTC time.
from lorawan_connection import AddedEvent
added = AddedEvent(descriptor=descriptor)
assert added.dev_eui == descriptor.dev_eui

Device metadata changed, such as its name or assigned profile.

Fixed type: EventType.UPDATED ("updated").

Field Type Meaning
descriptor DeviceDescriptor Updated device metadata.
received_at datetime Event timestamp. Defaults to the current UTC time.

A device left the subscription.

Fixed type: EventType.REMOVED ("removed").

Field Type Meaning
descriptor DeviceDescriptor Last known device metadata.
received_at datetime Event timestamp. Defaults to the current UTC time.

Each class adds the fields below to the shared fields. received_at is required. descriptor defaults to None. Supply either a descriptor or both network_id and dev_eui.

Backends convert SDK messages into these classes before delivery. Use the same classes for live events, tests, and capture replays.

Raw application bytes received from a device. The device library decides which ports and byte layouts it supports. data retains the bytes without copying. Mutable SDK messages are not exposed.

Fixed type: EventType.UPLINK ("up").

Field Type Default
data bytes Required
f_port int 1
from datetime import UTC, datetime
from lorawan_connection import UplinkEvent
event = UplinkEvent(
network_id="home",
dev_eui="0201010101010101",
received_at=datetime.now(UTC),
data=bytes.fromhex("01011098530000010210A87A0000AF51"),
f_port=1,
)

A device joined the network. dev_addr is the joined device address.

Fixed type: EventType.JOIN ("join").

Field Type Default
dev_addr str Required

Device battery and radio link status. margin is the radio link margin in dB. battery_level is a percentage. Check the power-source and battery-unavailable flags before reading it. Zero with battery_level_unavailable=False is a measured 0 percent charge. The same value with battery_level_unavailable=True means the level is unknown.

Fixed type: EventType.STATUS ("status").

Field Type Default
margin int 0
external_power_source bool False
battery_level_unavailable bool True
battery_level float 0

The base Device retains this report and notifies listeners automatically. See status properties.

The outcome of a confirmed downlink. Match queue_item_id to the returned command ID and check acknowledged.

Fixed type: EventType.ACK ("ack").

Field Type Default
queue_item_id str Required
acknowledged bool Required

See sending commands for command submission.

A gateway acknowledged transmission. This does not confirm device receipt.

Fixed type: EventType.TX_ACK ("txack").

Field Type Default
gateway_id str Required
downlink_id int Required

A backend log message. level and code retain the backend’s numeric values.

Fixed type: EventType.LOG ("log").

Field Type Default
description str Required
level int Required
code int Required

A device location estimate. latitude and longitude use degrees. altitude uses meters.

Fixed type: EventType.LOCATION ("location").

Field Type Default
latitude float Required
longitude float Required
altitude float Required

Call await collection.async_setup() to subscribe to events for the vendors represented by the collection’s model classes. A collection without model classes creates generic devices for all inventory exposed by the connection. The collection creates supported models and routes each event to its device automatically. The ChirpStack connection example shows connection startup and collection setup.

The subscription first delivers existing devices as ADDED events. For each supported device, the collection creates a model and calls its device-added listeners. Applications use those listeners to observe the models and subscribe to state updates before the first reading arrives.

Later activity goes to the existing model. An uplink can update several attributes, then the model calls notify() so listeners can read the new values. Device changes arrive through the same feed. REMOVED closes and removes the model. The quickstart shows this sequence with a descriptor and a captured uplink.

The descriptor must arrive before activity for an unknown device. If the backend first learns about a device through activity, it refreshes its device list before forwarding that activity. Collections ignore activity for devices they have not created. A backend only reports removals after a complete successful device-list refresh. An incomplete read must not make devices disappear.

The application owns credentials and the connection lifecycle. Create one collection per logical network. A collection can span several server applications.

Register a connection-loss listener with connection.on_disconnect(listener). The callback takes no arguments. Close the old collection when the connection is lost. A new connection supplies existing devices to a new collection. Events missed during the disconnect are not recovered.

Event callbacks and model listeners run synchronously on the caller’s thread or event loop. Keep network I/O in async connection and command methods. Pass regular functions as callbacks, and use the collection on one thread.