In bacpypes3, class TimeValue(Sequence) is the Python implementation of the ASHRAE 135 BACnetTimeValue construct — a SEQUENCE of two named elements, time (a BACnet Time) and value (an AnyAtomic). It lives in the bacpypes3.basetypes module and inherits the metaclass that collects those elements into _elements. A TimeValue says "at this time of day, the scheduled value becomes this," which is why you see it inside DailySchedule entries of a Schedule object's Weekly_Schedule. Most errors constructing one come from passing a raw Python value where the value element needs a BACnet atomic primitive.
You're Reading the Source and Hit This Class
You open the bacpypes3 base-types module to understand how a Schedule encodes, and you land on:
class TimeValue(Sequence):
...The question that usually brings people here is simple: what are the fields, why is it a Sequence, and what do I pass to build one? You may have arrived after a TypeError while constructing a schedule, or a peer device rejecting your WriteProperty with invalid-data-type even though the encode succeeded locally. Both trace back to the same two-field structure, so it is worth reading the class against the spec rather than guessing at the constructor.
What TimeValue Is in ASHRAE 135
BACnet's data types are specified in ASHRAE Standard 135 using ASN.1 notation. BACnetTimeValue is a SEQUENCE of exactly two components:
time— a BACnetTime. The time of day at which the paired value takes effect.value— the value to apply at that time. In the standard this slot is an abstract type restricted to atomic primitives; bacpypes3 models that restriction with theAnyAtomictype, which accepts a single atomic value (Null, Boolean, Unsigned, Integer, Real, Double, OctetString, CharacterString, BitString, Enumerated, Date, Time, or ObjectIdentifier) and nothing structured.
That pairing is the whole point of the type: it expresses a single scheduled transition. A list of TimeValue entries, in ascending time order, is what a DailySchedule carries for one day. Seven DailySchedule entries make up the Weekly_Schedule of a Schedule object. So a single TimeValue is the smallest unit of a BACnet schedule.
Why It Subclasses Sequence
Sequence in bacpypes3 is defined with a metaclass that, at class-creation time, scans the class for its declared components and records them in a class-level _elements collection mapping each element name to its type. Every Sequence subclass inherits that behavior, so TimeValue gets element collection, ordered encode, and decode for free. Declaring TimeValue as a Sequence with a time element typed Time and a value element typed AnyAtomic is all the library needs to know how to walk the two components on the wire.
This is the same machinery that backs class Choice(Sequence); a CHOICE reuses the element collection but constrains it to one active element. A TimeValue is a plain SEQUENCE: both elements are present and both are encoded, in order.
The Two Element Types You Have to Get Right
Because TimeValue is just a container, building one correctly is entirely about supplying the right type for each element.
- The
timeelement is a bacpypes3Time, not adatetime.time. In bacpypes3 the BACnet Time primitive is a four-field value —(hour, minute, second, hundredth)— with255as the per-field "unspecified" wildcard. If you are unsure how to construct it, the bacpypes3 Time class reference covers the 4-tuple form and the common4-tuple expectederror. - The
valueelement is an atomic primitive, wrapped. Pass a bacpypes3 primitive instance (Real(72.0),Boolean(True),Unsigned(3),Enumerated(...)), not a bare Pythonfloatorbool. Whether theAnyAtomicslot auto-coerces a primitive or needs an explicit cast has changed across bacpypes3 releases, so confirm the behavior in your installed version — the AnyAtomic class reference explains why the slot is an open atomic container and how to read a native value back out.
How to Read the Definition for Your Release
bacpypes3 is pre-1.0, and constructor signatures and accessor names have shifted between releases. Rather than copy a signature from a tutorial, read the class that ships with your pinned version:
- Find the class. After
pip install bacpypes3, open thebacpypes3.basetypesmodule in your site-packages and search forclass TimeValue. Thetimeandvalueelement declarations are right there. - Confirm the version. Cross-check against the bacpypes3 PyPI page, which links to the source repository for the currently published release. Pin the version in your project so the source you read matches the code you run.
- Treat ASHRAE 135 as the shape of record. The Python class mirrors the spec's SEQUENCE. If the class and the standard ever disagree, the standard wins for interoperability with other vendors' devices.
Common Pitfalls
- Passing a raw Python value as
value. bacpypes3 needs to know which BACnet primitive to encode. Wrap it —Real(72.0), not72.0— rather than relying on coercion that may not exist in your release. - Putting a structured type in
value. The slot isAnyAtomic; a BACnetObjectPropertyReference, a BACnetAddress, or any constructed SEQUENCE will not encode here. The schedule mechanism is built to drive one atomic property, typically aPresent_Value. - Using a
datetime.timefor thetimeelement. Use the bacpypes3Timeprimitive. Some recent releases accept a coercion, but the version-stable form is the explicit BACnet type. - Type-mismatching the schedule's target. The value in a
TimeValuemust be compatible with the property the schedule writes (its object-property reference). Sending aBooleanto a Real-typed point is a common cause of a post-write rejection even though the local encode looked fine. - Confusing bacpypes3 with the legacy bacpypes. The original
bacpypesandbacpypes3are separate packages with overlapping class names but different import paths and constructors. Check thatpip listshows the one you intend.
When to Escalate
If your TimeValue entries match the spec and the local encode succeeds but a peer device still rejects the schedule, the limitation is usually on the remote side. Escalate by:
- Capturing the
WritePropertyand the error reply in Wireshark and reading the BACnet error class and code in the response. - Checking the controller's Protocol Implementation Conformance Statement (PICS) to confirm it supports Schedule object writes and which atomic value types it accepts.
- Filing an issue on the bacpypes3 repository (linked from PyPI) only after ruling out a peer-side limitation, with a minimal reproducible example and the library version pinned.
Source Attribution
The technical guidance in this entry is grounded in the following public sources:
- bacpypes3 on PyPI — the canonical project page; links to the source repository where
TimeValueis defined in the base-types module for the currently published release. - ASHRAE Standard 135 — BACnet—A Data Communication Protocol for Building Automation and Control Networks. Normative reference for the
BACnetTimeValueSEQUENCE (itstimeandvaluecomponents) and the Schedule, DailySchedule, and Weekly_Schedule constructs that contain it. - ASHRAE 135 standard listing — where to obtain the current published version of the BACnet standard.
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.