API reference¶
Client for the BACnet/IP REST proxy (https://github.com/stblassitude/bacnet-ip-rest-proxy).
Client
¶
Client(
url: str,
token: str | None = None,
*,
timeout: float = 30.0,
verify: bool | str = True,
retry: RetryPolicy = RetryPolicy(),
concurrency: int = 10,
transport: AsyncBaseTransport | None = None,
)
A client of one BACnet/IP REST proxy, playing the part of a
bacpypes3 Application: read_property() and write_property() take
the same arguments as bacpypes3's, except that the device is addressed
by the proxy's device id (a configured alias, or a hostname/IP[:port])
instead of a BACnet Address.
Where the proxy works differently from BACnet, so does this class:
- Errors are raised, never returned (see errors.py).
list_objects()returns the proxy's cached object catalog, including each object's name and the caller'saccessto it, in one request.- There is no ReadPropertyMultiple.
read_properties()issues one HTTP request per property instead, concurrently, and reports each one's outcome separately. - Every request goes through the proxy's authorization, based on
token. A client acting for different users needs one Client per user token. - Transient failures are retried (see RetryPolicy and is_transient()), writes included: a WriteProperty of the same value at the same priority is idempotent.
Use as an async context manager, or call aclose() when done.
url is the proxy's base URL, without the /api/v1 path.
token is sent as a bearer token: one of the proxy's configured
tokens, or a JWT it accepts (e.g. a user's OIDC access token).
timeout is the default per-request timeout in seconds; each call
can override it. verify is passed to httpx: False to accept a
self-signed certificate, or the path of a CA bundle.
concurrency bounds how many requests this client has in flight
at once, across all calls. The proxy serializes what it sends to
the device itself; this keeps a large read_properties() from
opening hundreds of connections to the proxy. A request waiting
out a retry delay doesn't hold a slot.
transport replaces httpx's network transport, e.g. with an
httpx.MockTransport in tests.
list_devices
async
¶
The device ids (aliases) configured on the proxy.
read_device
async
¶
A device's identity summary, as the proxy's DeviceInfo JSON:
instance, object-name, vendor-name, model-name, access.
list_objects
async
¶
list_objects(
device: str, *, timeout: float | None = None
) -> list[ObjectSummary]
Every object on the device the caller may read, from the proxy's cache (refreshed proxy-side, by default every 60s). Objects the caller may not read are left out; if that is all of them, this raises ForbiddenError.
read_object
async
¶
read_object(
device: str,
objid: ObjectIdentifierLike,
*,
timeout: float | None = None,
) -> dict[str, Any]
A summary of one object, read by the proxy in a single
ReadPropertyMultiple: the proxy's ObjectInfo JSON, keyed by
property name (object-name, present-value, description,
units, status-flags), plus access. Properties the object
doesn't have are left out.
read_property
async
¶
read_property(
device: str,
objid: ObjectIdentifierLike,
prop: str,
array_index: int | None = None,
*,
timeout: float | None = None,
) -> Any
Read one property (bacpypes3: read_property(address, objid,
prop, array_index)) and return its value, decoded as described in
values.py. prop is the property name, e.g. "present-value", or
"property(N)" for one the proxy has no name for.
read_properties
async
¶
read_properties(
device: str,
requests: Iterable[PropertyRequest],
*,
timeout: float | None = None,
) -> list[Any]
Read several properties concurrently, one request each, and return a list in the same order holding each value or, for a read that failed, its ProxyError. Unlike a BACnet ReadPropertyMultiple this is not a single transaction: each value is read at a slightly different moment, and each read succeeds or fails on its own.
write_property
async
¶
write_property(
device: str,
objid: ObjectIdentifierLike,
prop: str,
value: Any,
priority: int | None = None,
*,
timeout: float | None = None,
) -> None
Write one property (bacpypes3: write_property(address, objid,
prop, value, array_index, priority), minus array_index, which
the proxy's write endpoint doesn't take).
value uses the same JSON shapes a read returns. None
relinquishes priority on a commandable property instead of
writing a value. priority (1-16) applies to commandable
properties only; the proxy uses 16 if it's omitted.
RetryPolicy
dataclass
¶
How often, and how patiently, a transient failure is retried.
attempts counts the first try, so RetryPolicy(attempts=1) disables
retrying. The delay before retry n (1-based) is base_delay * 2**(n-1),
capped at max_delay: 1+2+4+8 = 15s in total with the defaults.
BACnetError
¶
BACnetTimeoutError
¶
BadRequestError
¶
CommunicationError
¶
Bases: ProxyError
The proxy could not be reached, or its reply could not be decoded (e.g. an empty or truncated 200 body). The underlying exception is chained as cause.
ForbiddenError
¶
ForbiddenError(
status_code: int,
message: str,
error_class: int | None = None,
error_code: int | None = None,
)
Bases: ProxyStatusError
403: the credentials were accepted, but the proxy's authorization rules deny this request -- e.g. a write to a variable the token only has readonly access to.
NotFoundError
¶
ProxyStatusError
¶
ProxyStatusError(
status_code: int,
message: str,
error_class: int | None = None,
error_code: int | None = None,
)
Bases: ProxyError
The proxy answered with an HTTP error status.
UnauthorizedError
¶
UnauthorizedError(
status_code: int,
message: str,
error_class: int | None = None,
error_code: int | None = None,
)
Bases: ProxyStatusError
401: the proxy did not accept the credentials (missing, invalid or expired token).
ValueRejectedError
¶
Access
¶
Bases: StrEnum
The caller's effective right on an object, according to the proxy's authorization rules -- a proxy concept with no BACnet equivalent. It says nothing about whether the device will accept a write.
ObjectIdentifier
¶
Bases: NamedTuple
A BACnetObjectIdentifier: object type and instance number.
Like bacpypes3's ObjectIdentifier this is a 2-tuple, so objtype,
instance = objid works the same way, and it parses bacpypes3's
"analog-value,1" string form. Unlike bacpypes3, the type is kept as the
string the proxy uses on the wire (e.g. "analog-value", or
"object-type(N)" for a type without a name) rather than an ObjectType
enumeration -- the client has no local object-type registry, the proxy
does all of that.
parse
classmethod
¶
Accepts an ObjectIdentifier, a (type, instance) tuple, or a
string "type,instance" (bacpypes3's form) or "type:instance".
ObjectSummary
dataclass
¶
ObjectSummary(
objid: ObjectIdentifier,
name: str | None = None,
description: str | None = None,
units: Any = None,
access: Access | None = None,
)
One entry of Client.list_objects(): an object on the device with
the metadata the proxy caches for it. There is no bacpypes3 equivalent;
with plain BACnet this takes a walk of the device's object-list plus a
read of each object's properties.
is_transient
¶
is_transient(exc: ProxyError) -> bool
Whether a failed request is worth retrying: the proxy couldn't be reached or sent a garbled reply, the device timed out, or the device answered with an error other than "no such object/property". Any other status (400/401/403/404/422) is a property of the request itself and would fail again the same way.
decode_value
¶
Decode a property value as received from the proxy.