The Schedule object is the heart of time-based automation in BACnet. It contains a
weekly schedule (seven day schedules, each a list of time/value pairs) plus a list
of exception schedules (holidays, special events) that can reference
BACnet.Protocol.ObjectTypes.Calendar objects or contain inline date patterns.
At the scheduled times the object automatically writes the configured values
into the target properties listed in list_of_object_property_references.
The present_value of the schedule reflects the currently effective value (or
schedule_default outside any defined period).
Schedules are the standard way to implement occupancy-based setpoints,
lighting scenes, etc.
Object Description (ASHRAE 135)
The Schedule object type defines a standardized object used to describe a periodic schedule that may recur during a range of dates, with optional exceptions at arbitrary times on arbitrary dates. The Schedule object also serves as a binding between these scheduled times and the writing of specified "values" to specific properties of specific objects at those times.
Schedule objects that support intrinsic reporting shall apply the NONE event algorithm.
Behaviour and Operation
Schedule objects are active time-based actuators. The device must run a scheduler engine (typically a periodic task driven by the real-time clock) that:
- Evaluates the
weekly_scheduleand anyexception_scheduleentries (which may reference calendars). - Determines the currently effective value (or falls back to
schedule_default). - Writes that value to every property listed in
list_of_object_property_references, using the priority slot given bypriority_for_writing.
The readonly present_value of the Schedule itself reflects what it is currently
writing (or would write). The local application does not write to the targets
directly for scheduled control; the Schedule object performs the writes.
out_of_service can disable scheduled writes while still allowing the schedule
state to be inspected. When intrinsic reporting is enabled the Schedule can
participate in (minimal) event reporting for reliability issues.
Developer Implementation Notes (geared to device server / application authors)
The generated code handles storage + basic mechanics (validation, implicit_relationships,
readonly annotations as hints to your server, etc.). You must drive "special" live
properties and side effects yourself, analogous to maintaining present_value on
inputs via update_property/3 (never direct mutation). Read notes below + generated
tables for details.
Special / live properties and expected developer behaviour
present_value: The currently effective scheduled value for the referenced objects. Dev must: Your scheduler task must evaluate the weekly/exception schedules against current time (respect effective_period, special events, calendars), determine the value (or schedule_default), write it topresent_value, and perform the actual writes to all entries inlist_of_object_property_referencesat thepriority_for_writing.weekly_schedule,exception_schedule,schedule_default,effective_period,list_of_object_property_references,priority_for_writing, etc.: The schedule data and targets. Dev must: Populate at creation/config time (at minimum weekly or exception schedule present enforced).status_flags,reliability,out_of_service: Dev must:out_of_servicemeans "ignore schedule, do not write targets". Reliability for problems like missing calendar ref. Thein_alarm/fault/out_of_servicebits ofstatus_flagsare auto-managed by the object (overriddenlocal).
Schedule is one of the few objects that is expected to have side effects on other objects on a time base. The object itself is mostly passive data; the "active" part lives in your scheduler engine.
You own the scheduler engine: You must periodically:
- Read the current wall time (respect DST, the device's
utc_offset, etc.). - Evaluate
effective_period, then theweekly_schedule[ weekday ]plus any matchingexception_scheduleentries (aSpecialEventcan be a calendar reference or an inline date/time pattern + a list ofTimeValue). - Compute the effective value (or
schedule_default). - For every entry in
list_of_object_property_references, perform a WriteProperty (or internalupdate_property/3/set_priority/3if local) to the target property at the priority given bypriority_for_writing. - Update the Schedule's own
present_value, so that observers see what the schedule is currently "commanding".
Because the writes are performed at a specific priority, downstream objects (setpoints, binary outputs, etc.) will see the schedule's command only when no higher-priority source has a value in their priority array.
Functional schedule: At creation time either weekly_schedule or
exception_schedule (or both) must be supplied; otherwise you get an error.
This is a developer convenience, so you don't create a completely inert schedule.
out_of_service for a scheduler: When true, the engine should stop the
internal calculation and will stop performing the writes to the target properties
(they will keep whatever value they had at the moment it was disabled,
subject to their own priority arrays and relinquish defaults).
You can still change the schedule data (weekly list, targets, …)
while it is out of service.
Changing targets or exceptions at runtime:
Because list_of_object_property_references, exception_schedule,
weekly_schedule, etc. are normal writable properties,
a config tool or another schedule can rewrite them. Your engine simply sees the
new data on the next evaluation cycle. There is no "reload" signal; the object
is the source of truth.
Intrinsic NONE + reliability: The only intrinsic algorithm a Schedule uses is
NONE (see the See Also). It is intended for the Schedule object itself to be
able to report reliability problems (bad calendar reference, malformed
SpecialEvent, etc.) via the normal event machinery. Your engine should set
the Schedule's :reliability when it detects problems during evaluation.
Remote targets: A DeviceObjectPropertyRef in the list can point at another
device. Your engine must then issue a real WriteProperty service request
at the schedule's priority. Failures should probably be reflected
in the Schedule's reliability.
The object also has an effective_period (DateRange) that can be used to
make a whole schedule active only between two dates (e.g. "this schedule is
only valid during the 2025-2026 heating season").
Intrinsic Reporting
When intrinsic_reporting: true is passed to create/4, the Schedule participates
in intrinsic event reporting using the NONE algorithm
(primarily for reliability/fault conditions).
Examples
Creating a basic Schedule:
iex> {:ok, sch} = BACnet.Protocol.ObjectTypes.Schedule.create(600, "Lighting", %{}); sch.object_name
"Lighting"See Also
BACnet.Protocol.DailyScheduleBACnet.Protocol.DateRangeBACnet.Protocol.EventAlgorithms.NoneBACnet.Protocol.ObjectTypes.CalendarBACnet.Protocol.SpecialEventBACnet.Protocol.TimeValue
The following part has been automatically generated.
Click to expand
This module defines a BACnet object of the type `schedule`. The following properties are defined: | Property | Revision | Required | Readonly | Protected | Intrinsic | |----------|----------|----------|----------|-----------|-----------| | acked_transitions | | | X | | X | | description | | | | | | | effective_period | | X | | | | | event_algorithm_inhibit | | | | | X | | event_algorithm_inhibit_ref | | | | | X | | event_detection_enable | | | | | X | | event_enable | | | | | X | | event_message_texts | | | X | | X | | event_message_texts_config | | | | | X | | event_timestamps | | | X | | X | | exception_schedule | | | | | | | limit_enable | | | | | X | | list_of_object_property_references | | X | | | | | notification_class | | | | | X | | notify_type | | | | | X | | object_instance | | X | X | | | | object_name | | X | X | | | | out_of_service | | X | | | | | present_value | | X | | | | | priority_for_writing | | X | | | | | profile_location | 19 | | | | | | profile_name | | | | | | | reliability | | X | | | | | reliability_evaluation_inhibit | | | | | | | schedule_default | | X | | | | | status_flags | | X | X | | | | tags | 19 | | | | | | time_delay | | | | | X | | time_delay_normal | | | | | X | | weekly_schedule | | | | | | The following properties have additional semantics: | Property | Has Default | Has Init | Implicit Relationships | Validators | Annotations | |----------|-------------|----------|------------------------|------------|-------------| | effective_period | X | | | | | | event_algorithm_inhibit_ref | | | event_algorithm_inhibit | | | | list_of_object_property_references | X | | | | | | present_value | X | | | | | | priority_for_writing | X | | | | | | profile_location | | | | Fun | `revision: 19` | | schedule_default | X | | | | | | tags | | | | | `revision: 19` | | weekly_schedule | | X | | | | The following table shows the default values and/or init functions: | Property | Default Value | Init Function | |----------|---------------|---------------| | effective_period | `%BACnet.Protocol.DateRange{...}` | | | list_of_object_property_references | `%BACnet.Protocol.BACnetArray{...}` | | | present_value | `%BACnet.Protocol.ApplicationTags.Encoding{...}` | | | priority_for_writing | `16` | | | schedule_default | `%BACnet.Protocol.ApplicationTags.Encoding{...}` | | | weekly_schedule | | `Utility.Internal.init_fun_schedule_weekly_schedule/0` |Summary
Types
Common object options for creation - all are optional.
Options accepted when creating or configuring a Schedule object.
Available property names for this object.
The structure for property errors.
Represents a Schedule object. All keys should be treated as read-only,
all updates should go only through update_property/3.
Functions
Adds an optional property to an object. Remote objects can not be mutated using this operation.
Creates a new object struct with the defined properties. Optional properties are not
created when not given, only required, given and dependency properties are created.
Properties with a value of nil are ignored.
Auto generated function to get the names of all properties this object supports.
Auto generated function to get the annotations for the given property name.
Auto generated function to get the list of annotations for each property.
Auto generated function to get the names of properties used for COV reporting.
Auto generated function to get the names of intrinsic properties.
Get the BACnet object identifier.
Auto generated function to get the names of optional properties.
Get the list of properties the object has.
Auto generated function to get a map of property name to type.
Get a property's value from an object.
Auto generated function to get the names of protected properties.
Auto generated function to get the names of readonly properties.
Auto generated function to get the names of required properties.
Checks if the given object has the given property.
Checks if the given object has Intrinsic Reporting enabled.
Checks if the given property is writable.
Removes an optional property from an object. This function is idempotent. Remote objects can not be mutated using this operation.
Auto generated function to check whether the object type supports intrinsic reporting.
Updates a property of an object.
Types
@type common_object_opts() :: {:allow_numeric_constants, boolean()} | {:allow_unknown_properties, boolean()} | {:ignore_unknown_properties, boolean()} | {:revision, BACnet.Protocol.Constants.protocol_revision()} | {:skip_property_validation_remote_object, boolean() | :value}
Common object options for creation - all are optional.
allow_numeric_constants- Constants are atoms and thus unknown constants or vendor extensions are integers and thus are rejected. Enabling this option will allow integers (non_neg_integer()) for properties with aConstants.type()spec.allow_unknown_properties- Properties that are unknown to the object implementation are usually rejected. With this option, unknown properties (numeric identifiers usually means we dont know them) are accepted and put into a separate map. This does mean we can not validate or write them. Types of the values can be anything at this point. While you can read unknown properties with atom or integer as property identifier, you can only remove numeric unknown property identifiers from an object. Property identifiers of typeatomare only accepted, if it is a remote object (object implementation is only enforced if it is a local object). Numeric property identifiers are accepted regardless of remote object or not. For remote objects, this means you have to write "raw values" (usuallyEncodingstructs).ignore_unknown_properties- Properties that are unknown to the object implementation are usually rejected. With this option, unknown properties get ignored, as if they were not specified.revision- The BACnet protocol revision to check required properties against. Optional properties are regardless of revision available. SeeBACnet.Protocol.Constants.protocol_revision/0for the available revisions.skip_property_validation_remote_object- Skips property validation for remote objects. Sometimes it is possible that the value is invalid as per BACnet specification (i.e. value 0 for a multistate object), but you still want those to be represented. Valuetrueneither type nor value are validated. Value:valuemeans the type is still validated and only the value validator is not run (if present). The property'svalidator_funwill also be skipped.
@type object_opts() :: common_object_opts()
Options accepted when creating or configuring a Schedule object.
@type property_name() ::
:acked_transitions
| :description
| :effective_period
| :event_algorithm_inhibit
| :event_algorithm_inhibit_ref
| :event_detection_enable
| :event_enable
| :event_message_texts
| :event_message_texts_config
| :event_timestamps
| :exception_schedule
| :limit_enable
| :list_of_object_property_references
| :notification_class
| :notify_type
| :object_instance
| :object_name
| :out_of_service
| :present_value
| :priority_for_writing
| :profile_location
| :profile_name
| :reliability
| :reliability_evaluation_inhibit
| :schedule_default
| :status_flags
| :tags
| :time_delay
| :time_delay_normal
| :weekly_schedule
Available property names for this object.
@type property_update_error() :: {:error, {error :: atom(), property :: BACnet.Protocol.Constants.property_identifier()}}
The structure for property errors.
@type t() :: %BACnet.Protocol.ObjectTypes.Schedule{ _metadata: internal_metadata(), _unknown_properties: %{ optional(atom() | non_neg_integer()) => term() | BACnet.Protocol.ApplicationTags.Encoding.t() | [BACnet.Protocol.ApplicationTags.Encoding.t()] }, acked_transitions: BACnet.Protocol.EventTransitionBits.t() | nil, description: String.t() | nil, effective_period: BACnet.Protocol.DateRange.t(), event_algorithm_inhibit: boolean() | nil, event_algorithm_inhibit_ref: BACnet.Protocol.ObjectPropertyRef.t() | nil, event_detection_enable: boolean() | nil, event_enable: BACnet.Protocol.EventTransitionBits.t() | nil, event_message_texts: BACnet.Protocol.EventMessageTexts.t() | nil, event_message_texts_config: BACnet.Protocol.EventMessageTexts.t() | nil, event_timestamps: BACnet.Protocol.EventTimestamps.t() | nil, exception_schedule: BACnet.Protocol.BACnetArray.t(BACnet.Protocol.SpecialEvent.t()) | nil, limit_enable: BACnet.Protocol.LimitEnable.t() | nil, list_of_object_property_references: BACnet.Protocol.BACnetArray.t(BACnet.Protocol.DeviceObjectPropertyRef.t()), notification_class: non_neg_integer() | nil, notify_type: BACnet.Protocol.Constants.notify_type() | (reserved_or_vendor_extension :: non_neg_integer()) | nil, object_instance: non_neg_integer(), object_name: String.t(), out_of_service: boolean(), present_value: BACnet.Protocol.ApplicationTags.Encoding.t(), priority_for_writing: 1..16, profile_location: String.t() | nil, profile_name: String.t() | nil, reliability: BACnet.Protocol.Constants.reliability() | (reserved_or_vendor_extension :: non_neg_integer()), reliability_evaluation_inhibit: boolean() | nil, schedule_default: BACnet.Protocol.ApplicationTags.Encoding.t(), status_flags: BACnet.Protocol.StatusFlags.t(), tags: BACnet.Protocol.BACnetArray.t(BACnet.Protocol.NameValue.t()) | nil, time_delay: non_neg_integer() | nil, time_delay_normal: non_neg_integer() | nil, weekly_schedule: BACnet.Protocol.BACnetArray.t(BACnet.Protocol.DailySchedule.t(), 7) | nil }
Represents a Schedule object. All keys should be treated as read-only,
all updates should go only through update_property/3.
Functions
@spec add_property(t(), BACnet.Protocol.Constants.property_identifier(), term()) :: {:ok, t()} | property_update_error()
Adds an optional property to an object. Remote objects can not be mutated using this operation.
Please note that properties of services can not be dynamically added and instead
the object must be newly created using create/4.
@spec create( non_neg_integer(), String.t(), %{optional(property_name() | atom() | non_neg_integer()) => term()}, [object_opts() | internal_metadata()] ) :: {:ok, t()} | property_update_error()
Creates a new object struct with the defined properties. Optional properties are not
created when not given, only required, given and dependency properties are created.
Properties with a value of nil are ignored.
Only properties that are required for specific services (i.e. Intrinsic Reporting) are automatically created.
@spec get_all_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of all properties this object supports.
@spec get_annotation(property_name()) :: [term()]
Auto generated function to get the annotations for the given property name.
@spec get_annotations() :: [{name :: property_name(), values :: [term()]}]
Auto generated function to get the list of annotations for each property.
@spec get_cov_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of properties used for COV reporting.
@spec get_intrinsic_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of intrinsic properties.
@spec get_object_identifier(t()) :: BACnet.Protocol.ObjectIdentifier.t()
Get the BACnet object identifier.
@spec get_optional_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of optional properties.
@spec get_properties(t()) :: [BACnet.Protocol.Constants.property_identifier()]
Get the list of properties the object has.
@spec get_properties_type_map() :: map()
Auto generated function to get a map of property name to type.
@spec get_property( t(), BACnet.Protocol.Constants.property_identifier() | non_neg_integer() ) :: {:ok, term()} | property_update_error()
Get a property's value from an object.
@spec get_protected_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of protected properties.
Protected is an annotation and the object modules prevent writing to this property directly in code. The protected properties are either written on creation or updated automatically depending on other properties being written to. Some properties are only written once at creation and never updated.
@spec get_readonly_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of readonly properties.
Readonly is only an annotation that the property should be write protected
on the BACnet side, there is no actual write protection in the object.
This is a hint to the device server. If you need actual write protection, see protected.
@spec get_required_properties() :: [BACnet.Protocol.Constants.property_identifier()]
Auto generated function to get the names of required properties.
@spec has_property?(t(), BACnet.Protocol.Constants.property_identifier()) :: boolean()
Checks if the given object has the given property.
See BACnet.Protocol.ObjectsUtility.has_property?/2 for implementation details.
Checks if the given object has Intrinsic Reporting enabled.
@spec property_writable?(t(), BACnet.Protocol.Constants.property_identifier()) :: boolean()
Checks if the given property is writable.
Check BACnet.Protocol.ObjectsUtility.property_writable?/2 for a basic run-down.
@spec remove_property( t(), BACnet.Protocol.Constants.property_identifier() | non_neg_integer() ) :: {:ok, t()} | property_update_error()
Removes an optional property from an object. This function is idempotent. Remote objects can not be mutated using this operation.
Please note that properties of services can not be dynamically removed and instead
the object must be newly created using create/4. Required properties can not be removed.
@spec supports_intrinsic() :: boolean()
Auto generated function to check whether the object type supports intrinsic reporting.
@spec update_property(t(), BACnet.Protocol.Constants.property_identifier(), term()) :: {:ok, t()} | property_update_error()
Updates a property of an object.