The Analog Input object is the primary BACnet object type for exposing continuous
sensor measurements such as temperature, pressure, flow, humidity, current, voltage,
or any other physical analog quantity. Its present_value (a float) reflects the
current reading, scaled and in the units specified by the required units property.
Supporting properties include min_present_value/max_present_value limits,
resolution, update_interval, device_type (for the physical sensor), and
reliability/out_of_service handling.
Change-of-value (COV) reporting is supported and can be tuned with the cov_increment
property (only notifications are sent when the value changes by at least that amount).
When intrinsic_reporting: true is passed to create/4, the object activates
OUT_OF_RANGE intrinsic alarming with high_limit, low_limit, and deadband
properties plus the full event reporting machinery.
Object Description (ASHRAE 135)
The Analog Input object type defines a standardized object whose properties represent the externally visible characteristics of an analog input.
Analog Input objects that support intrinsic reporting shall apply the OUT_OF_RANGE event algorithm.
Behaviour and Operation
Analog Input objects are measurement objects. The present_value (a float in the
units declared by the required units property) is normally maintained by the local
application or I/O subsystem that reads the physical sensor and writes the current
value (subject to any scaling, filtering or calibration the device performs).
BACnet clients may freely read present_value, status_flags, reliability, etc.
Direct writes to present_value from the network side are only permitted while
out_of_service is true (this allows forcing a value for testing, simulation or
commissioning). When out_of_service is false, the device server must reject
writes to present_value coming over BACnet (the object struct itself does not
enforce this for input objects because it cannot distinguish local vs. remote
writers; see BACnet.Protocol.ObjectsMacro for guidance).
Setting out_of_service to true also signals that the physical input is
disconnected or ignored. The reliability property (together with status_flags.fault)
is used to indicate sensor or input hardware problems. COV reporting is supported and
can be tuned with cov_increment so that notifications are only generated for
changes large enough to matter.
When the object is created with intrinsic_reporting: true, the intrinsic event
properties become available and the object will use the OUT_OF_RANGE algorithm to
manage event_state transitions and generate notifications via the configured
notification_class.
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 measured analog value in engineering units. Dev must: Your I/O / ADC / sensor driver or polling task computes the value (apply calibration, scaling, filtering) and callsupdate_property(obj, :present_value, val)on samples or significant changes. This is the core "live input" contract.status_flags: Thein_alarm,faultandout_of_servicebits are automatically maintained by the object (based onevent_state,reliabilityand theout_of_serviceproperty). Theoverriddenbit is a local matter for the user to set when appropriate.out_of_service: Suspends real input. Dev must: Your driver ignores hardware whiletrue; your BACnet write handler allows forcing present_value only whiletrue.reliability(implicit toreliability_evaluation_inhibit): Sensor health. Dev must: Set based on hardware (e.g.:no_sensor,:over_range). Thefaultbit instatus_flagsis automatically set by the object. Re-evaluate intrinsic events on change.cov_increment: Affects when COV notifications are sent forpresent_valuechanges.Intrinsic only (
high_limit,low_limit,deadband, + event_ set): Limits for alarming. *Dev must: After PV/reliability update, your central event engine must run the OUT_OF_RANGE algorithm (using these params on the object), transitionevent_state, manage timestamps/acked, and send notifications if needed via notification_class. Object only stores; you execute.resolution,update_interval,min/max_present_value,device_type: Mostly static metadata/config. Dev must: Set at creation or allow writes; publish accurate info from your transducer characteristics.update_intervalcan reflect your scan rate.
See the rest of these notes and the generated tables (at end of moduledoc) for creation, runtime PV, write protection, COV, intrinsic, remote objects, etc.
Creation: create(instance, name, %{units: :degrees_celsius, ...}, intrinsic_reporting: true)
Optional fields like min_present_value, max_present_value, resolution, update_interval,
device_type, cov_increment can be supplied.
If intrinsic_reporting: true is passed in opts, the limit fields
(high_limit, low_limit, pulse_rate, limit_monitoring_interval)
plus the full intrinsic set (event_enable, etc.) become active and non-nil.
Runtime PV maintenance (the heart of an input object): Your I/O scanner, ADC
driver or periodic task is 100% responsible for the engineering value. Compute it
(apply any local calibration, filtering, unit conversion), then call
update_property(obj, :present_value, val). This will:
- run the validator (float range check)
- update
status_flags(in_alarm/fault/out_of_service) automatically as needed - return a (possibly mutated) object that you must persist back into your
object store / ETS / GenServer state.
Never do
%{obj | present_value: val}unless you are inside a controlled test path; you will bypass protection, validation, etc.
Network write protection for inputs (MANDATORY for conformance): The library
explicitly documents that input objects do not protect present_value themselves
because they cannot know whether the caller is "local app" or "BACnet wire".
Therefore your WriteProperty / WritePropertyMultiple / ConfirmedWriteProperty
service handler (or the generic property writer) must protect writes to present_value
when the object is not out of service.
Local driver code bypasses this check (it calls the function directly).
When out_of_service becomes true the protection is lifted, so a technician or
simulator can force a value for diagnostics. The out_of_service bit in
status_flags is automatically kept in sync by the object.
out_of_service + reliability contract: Setting out_of_service = true tells
everybody "ignore the real sensor, the value you see may be synthetic".
Your driver should stop (or ignore) hardware reads while it is true. You must
keep :reliability accurate at all times (:no_fault_detected,
:over_range, :under_range, :communication_failure, :process_error, ...). When
reliability is not the no-fault value the .fault bit of status_flags is
automatically set by the object (the overridden bit is a local matter).
reliability_evaluation_inhibit can be used to temporarily suppress reliability evaluation.
Intrinsic reporting / event generation: With intrinsic_reporting: true the
object carries a complete OUT_OF_RANGE event machine (high_limit, low_limit,
deadband, time_delay, time_delay_normal, event_enable, acked_transitions,
event_state, event_timestamps, notification_class, ...). After a PV update
(or a reliability change) your event-detection task must:
- Re-evaluate the OUT_OF_RANGE algorithm using the limit/deadband values on this object + the current PV.
- Possibly transition
event_state(normal <-> high-limit <-> low-limit). - Update timestamps, ack bits etc.
- If a transition that requires notification occurred, look up the NotificationClass object and send the appropriate notifications (using the priority and ack-required flags from the class). The object struct only stores the state; the evaluation and notification emission is server code (often centralised in an EventManager or similar). The same applies to the optional fault algorithm if you support one.
Other properties your driver may need to touch:
update_interval- you can publish how often you intend to refresh the value.device_type- static string describing the transducer ("10k thermistor", "4-20mA pressure transducer" …).resolution- the smallest change the hardware can reliably report.min_present_value/max_present_value- Engineering limits of the sensor. All of these are normally written once at creation/configuration time.
Remote objects: If _metadata.remote_object is set (populated by
BACnet.Protocol.ObjectsUtility when reading from a remote device), all mutation
operations (update_property, add_property, set_priority, etc.) will be rejected
by the generated code. You can only read remote analog inputs.
Threading / concurrency note: Your object store must handle concurrent reads
(many COV subscribers, ReadPropertyMultiple) while you are doing an update.
The returned object from update_property is the new canonical version; atomically
replace it.
Testing / simulation: The canonical way to force a value for a test is:
- Write
out_of_service = true(allowed even on inputs). - Write the desired
present_value. - Later set
out_of_service = falseto return to real hardware.
The generated moduledoc lists every field with its revision, required/readonly/protected/ intrinsic flags, default, init_fun, validator and annotations - use it as the authoritative reference when writing your driver.
See also the ObjectsMacro moduledoc for the generic rules that apply to all
objects (protected properties, common defaults, remote-object behaviour, etc.).
Intrinsic Reporting
When intrinsic_reporting: true is passed to create/4 (or the object is
configured that way), the following additional properties become active:
deadband, high_limit, low_limit, plus the standard event reporting set
(event_enable, event_state, etc.). The object will use the OUT_OF_RANGE event
algorithm.
COV Reporting
Change-of-value reporting is supported via the cov_increment property.
Examples
Creating a simple Analog Input (minimal properties):
iex> {:ok, ai} = BACnet.Protocol.ObjectTypes.AnalogInput.create(1, "Room Temp", %{units: :degrees_celsius}); ai.units
:degrees_celsiusEnabling intrinsic reporting (additional properties become available):
iex> {:ok, ai} = BACnet.Protocol.ObjectTypes.AnalogInput.create(2, "Pressure", %{
...> units: :pascals
...> }, intrinsic_reporting: true); ai.high_limit != nil
trueSee Also
The following part has been automatically generated.
Click to expand
This module defines a BACnet object of the type `analog_input`. The following properties are defined: | Property | Revision | Required | Readonly | Protected | Intrinsic | |----------|----------|----------|----------|-----------|-----------| | acked_transitions | | | X | | X | | cov_increment | | | | | | | deadband | | | | | X | | description | | | | | | | device_type | | | | | | | 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_state | | X | | | | | event_timestamps | | | X | | X | | high_limit | | | | | X | | limit_enable | | | | | X | | low_limit | | | | | X | | max_present_value | | | | | | | min_present_value | | | | | | | notification_class | | | | | X | | notify_type | | | | | X | | object_instance | | X | X | | | | object_name | | X | X | | | | out_of_service | | X | | | | | present_value | | X | | | | | profile_location | 19 | | | | | | profile_name | | | | | | | reliability | | | | | | | reliability_evaluation_inhibit | | | | | | | resolution | | | X | | | | status_flags | | X | X | | | | tags | 19 | | | | | | time_delay | | | | | X | | time_delay_normal | | | | | X | | units | | X | | | | | update_interval | | | | | | The following properties have additional semantics: | Property | Has Default | Has Init | Implicit Relationships | Validators | Annotations | |----------|-------------|----------|------------------------|------------|-------------| | cov_increment | X | | | | | | deadband | X | | | | | | event_algorithm_inhibit_ref | | | event_algorithm_inhibit | | | | high_limit | X | | | | | | low_limit | X | | | | | | present_value | X | | | Fun | `readonly_when: {:out_of_service, false}` | | profile_location | | | | Fun | `revision: 19` | | reliability | | | reliability_evaluation_inhibit | | | | resolution | X | | | | | | tags | | | | | `revision: 19` | | units | X | | | | | The following table shows the default values and/or init functions: | Property | Default Value | Init Function | |----------|---------------|---------------| | cov_increment | `0.1` | | | deadband | `0.0` | | | high_limit | `0.0` | | | low_limit | `0.0` | | | present_value | `0.0` | | | resolution | `0.1` | | | units | `:no_units` | |Summary
Types
Common object options for creation - all are optional.
Options accepted when creating or configuring an Analog Input object.
Available property names for this object.
The structure for property errors.
Represents an Analog Input 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() :: {:intrinsic_reporting, boolean()} | common_object_opts()
Options accepted when creating or configuring an Analog Input object.
In addition to the common object options, supports enabling intrinsic
reporting via intrinsic_reporting.
@type property_name() ::
:acked_transitions
| :cov_increment
| :deadband
| :description
| :device_type
| :event_algorithm_inhibit
| :event_algorithm_inhibit_ref
| :event_detection_enable
| :event_enable
| :event_message_texts
| :event_message_texts_config
| :event_state
| :event_timestamps
| :high_limit
| :limit_enable
| :low_limit
| :max_present_value
| :min_present_value
| :notification_class
| :notify_type
| :object_instance
| :object_name
| :out_of_service
| :present_value
| :profile_location
| :profile_name
| :reliability
| :reliability_evaluation_inhibit
| :resolution
| :status_flags
| :tags
| :time_delay
| :time_delay_normal
| :units
| :update_interval
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.AnalogInput{ _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, cov_increment: float() | nil, deadband: float() | nil, description: String.t() | nil, device_type: String.t() | nil, 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_state: BACnet.Protocol.Constants.event_state() | (reserved_or_vendor_extension :: non_neg_integer()), event_timestamps: BACnet.Protocol.EventTimestamps.t() | nil, high_limit: float() | nil, limit_enable: BACnet.Protocol.LimitEnable.t() | nil, low_limit: float() | nil, max_present_value: float() | nil, min_present_value: float() | nil, 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: float(), profile_location: String.t() | nil, profile_name: String.t() | nil, reliability: BACnet.Protocol.Constants.reliability() | (reserved_or_vendor_extension :: non_neg_integer()) | nil, reliability_evaluation_inhibit: boolean() | nil, resolution: float() | nil, 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, units: BACnet.Protocol.Constants.engineering_unit() | (reserved_or_vendor_extension :: non_neg_integer()), update_interval: non_neg_integer() | nil }
Represents an Analog Input object. All keys should be treated as read-only,
all updates should go only through update_property/3.
Properties which are for Intrinsic Reporting are nil, if disabled. If Intrinsic Reporting is enabled on the object, then the properties can not be nil.
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.
By default, a default cov_increment of 0.1 is used.
It is strongly advised to change this to something reasonable for the application.
@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.