In bacpypes3, Any is the open value container that models BACnet's ABSTRACT-SYNTAX.&Type — the ASHRAE Standard 135 datatype used wherever a property or service parameter can hold a value whose concrete type is not fixed by the schema. Unlike AnyAtomic, which is restricted to a single primitive, plain Any can carry either an atomic value or a constructed one (Sequence, Array, or CHOICE). It stores the raw, application-tagged content and defers decoding until you cast it to a known type, which is why reading one back often hands you a wrapper object rather than a native Python value.
The Scenario
You are reading bacpypes3 source — or your IDE jumped to a definition — and you land on a class named Any. The name is deliberately broad, and that is exactly what makes it confusing: it does not correspond to a single BACnet datatype the way Real or Unsigned does. It is a placeholder for "whatever value belongs here, decided at runtime."
This usually surfaces in one of two ways. Either you read a property back and got an Any instance where you expected a plain number, string, or object identifier; or you are constructing a service request or a complex object and bacpypes3 wants an Any for a field you thought took a concrete type. Both lead to the same question: what does this container actually hold, and how do I get a typed value in or out of it?
What Any Represents in BACnet
BACnet's abstract syntax (ASHRAE Standard 135) defines an open datatype written as ABSTRACT-SYNTAX.&Type. It marks a position in the protocol where the encoder cannot know the datatype in advance — the actual type is whatever the application or context tag on the wire says it is, and the receiver interprets it using out-of-band knowledge such as the property's declared datatype.
The clearest example is property access itself. In the ReadProperty and WriteProperty services, the property value is carried in a parameter typed as the open ABSTRACT-SYNTAX.&Type, because a single service has to move an analog setpoint, a binary state, a character string, or a structured value depending on which property you addressed. The service encoding is fixed; the value it carries is not. That open slot is what bacpypes3's Any models.
You also meet the open type inside constructed datatypes — places where a SEQUENCE or CHOICE includes a member whose type depends on context rather than being pinned by the schema. Wherever the standard writes the open type, bacpypes3 needs a class that can hold encoded content first and resolve its concrete type later. That class is Any.
Any vs. AnyAtomic
The two names cause most of the confusion, so it is worth being precise. Any is the general open container: it can hold an atomic (primitive) value or a constructed value. AnyAtomic is the same idea narrowed to exactly one primitive — no Sequence, no Array, no nested CHOICE. That is why, in the library, the restricted form inherits from the general one. If you have been reading about the Schedule object's setpoints, that is the AnyAtomic case; the entry on why bacpypes3 defines class AnyAtomic(Any) covers that restriction in detail.
Practically: if the BACnet property you are working with allows a constructed value, you want Any. If the standard restricts the slot to a single primitive, you want AnyAtomic, and supplying a constructed value will be rejected. Check the property's datatype in Standard 135 rather than guessing — the choice between the two follows directly from what the standard permits in that position.
Getting a Value In and Out
Because Any holds tagged content rather than a decoded value, working with it is a two-step process: you put a typed value in, and you cast a typed value out. The original BACpypes line (the pre-3 library) exposed this as explicit cast-in and cast-out operations. bacpypes3 reworked the encoding layer substantially, so the accessor names and call patterns are not guaranteed to match older examples. Rather than copy a method name from a blog post, confirm what your installed release actually provides.
The reliable approach when you read a property back and got an Any is to tell bacpypes3 the datatype you expect, so it can resolve the open value to a concrete class for you. The concrete type comes from the property you addressed — the standard, or the device's PICS, tells you whether that property is a Real, an Enumerated, a structured type, and so on. Once you know the target type, you cast the Any to it and read the native value.
How to Find the Definition in Your Installed Release
Module paths and accessor names have moved between bacpypes3 releases, so verify against the exact version you have installed rather than trusting a path from a guide. Locate the package directory and grep for the class:
# Find where bacpypes3 is installed
python -c "import bacpypes3, os; print(os.path.dirname(bacpypes3.__file__))"
# Grep that directory for the class definition
grep -rn "class Any" /path/printed/aboveYou will find Any defined alongside the other constructed and open types rather than next to the simple primitives, with AnyAtomic nearby as a subclass. Confirm the inheritance and the available methods at runtime without reading the file:
import inspect
from bacpypes3.constructeddata import Any # confirm this import against your release
print(inspect.getsourcefile(Any))
print(Any.__mro__)
print([m for m in dir(Any) if not m.startswith("__")])Treat the import line as something to verify, not copy blindly — if it raises ImportError, the grep above gives you the correct module for your version. The dir() output is the authoritative list of cast and accessor methods for the code actually running on your machine, which beats any documented method name that may have changed.
Common Pitfalls
- Expecting a native value back. A property or parameter typed as the open type does not return a bare Python
float,str, or tuple. It returns theAnywrapper, and you cast it to the concrete type to read the value. Comparing the wrapper directly to a number or string will not behave the way you expect. - Casting to the wrong target type.
Anydefers decoding, which means it cannot tell you the "right" type on its own — you supply that. Cast a value that is really an Enumerated as if it were an Unsigned and you may get a wrong-but-plausible number rather than an error. Derive the target type from the property's datatype in the standard or the device PICS. - Confusing Any with AnyAtomic. Plain
Anyaccepts constructed values;AnyAtomicdoes not. If a constructed value is being rejected, you may be holding anAnyAtomicwhere the property actually allows the broaderAny. - Reusing pre-3 BACpypes code. The original BACpypes cast methods and module layout do not carry over unchanged to bacpypes3. Confirm imports and method names against your installed release with the introspection above before debugging deeper.
- Assuming bacpypes3 knows every property's type. For proprietary or vendor-specific properties the library may have no registered datatype, so it cannot resolve the open value for you. In that case you provide the expected type explicitly — the device documentation is the source for it.
When to Escalate
If you have confirmed the class, the inheritance, and the cast methods but decoding still fails against a real device, the problem is usually the concrete type you are casting to or supplying, not Any itself. Capture the exchange and compare the application or context tag on the wire against what the device's PICS says the property carries; a mismatch there explains most failed casts.
When the class definition or accessors genuinely differ from what any guide describes, the source of truth is the bacpypes3 project. Check the issues and discussions on the bacpypes3 GitHub repository rather than guessing, since the maintainer documents behavior changes there. For wiring these types into a working client or server, see the bacpypes3 custom applications tutorial.
Source Attribution
The technical guidance in this entry is informed by the following sources:
- ASHRAE Standard 135 — BACnet—A Data Communication Protocol for Building Automation and Control Networks. The normative reference for the
ABSTRACT-SYNTAX.&Typeopen datatype and for the ReadProperty / WriteProperty service parameters that carry it. - bacpypes3 — GitHub (JoelBender/BACpypes3) — the library source and documentation; the authoritative reference for the
Anyclass definition, its methods, and the module layout in any given release. - bacpypes3 on PyPI — release listing for confirming which version is installed before relying on a specific import path or accessor name.
Additional testing and field validation by SiteConduit.
Was this article helpful?
Related Articles
Need to do this remotely? SiteConduit provides Layer 2 access that preserves BACnet broadcasts — no BBMD needed for remote sessions. Join the waitlist.
SiteConduit Technical Team
Idea Networks Inc.
SiteConduit builds managed remote access for building automation. Our knowledge base is maintained by BAS professionals with hands-on experience deploying and troubleshooting BACnet, Niagara, Modbus, and Facility Explorer systems.