BACnet.Protocol.ObjectTypes.BinaryInput (bacstack v0.1.0-dev.1)

Copy Markdown View Source

The Binary Input object exposes the state of a physical two-state sensor or contact (door switch, pump run status, high-level float, limit switch, etc.). The present_value is a Boolean (ACTIVE/INACTIVE or similar) that can be inverted from the physical polarity via the polarity property. A device_type string can document the underlying hardware.

The object can be marked as a physical input via metadata and supports out-of-service and reliability indication. When intrinsic_reporting: true is passed to create/4, CHANGE_OF_STATE intrinsic alarming is enabled (with optional time delay and event parameters). COV reporting is also supported for state changes.

Object Description (ASHRAE 135)

The Binary Input object type defines a standardized object whose properties represent the externally visible characteristics of a binary input. A "binary input" is a physical device or hardware input that can be in only one of two distinct states.

Binary Input objects that support intrinsic reporting shall apply the CHANGE_OF_STATE event algorithm.

Behaviour and Operation

Binary Input objects are physical or logical two-state sensor objects. The local application or I/O layer is responsible for reading the hardware contact / switch (respecting the polarity setting - using set_input/2) and updating present_value. The active_text / inactive_text properties provide human-readable labels for the two states.

Writes to present_value over BACnet are only allowed while out_of_service is true (for testing or simulation). The device server must enforce this protection for input objects. out_of_service also means the physical input is ignored.

Additional properties such as change_of_state_time, change_of_state_count and elapsed_active_time are maintained by the local logic as side effects of state changes. Reliability and status flags indicate contact or wiring problems.

When intrinsic_reporting: true the CHANGE_OF_STATE (and optional FAULT_STATE) alarming machinery is present.

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: Logical state after polarity. Dev must: Use the set_input/2 helper to correctly set the present value based on the logical state of the physical input.

  • status_flags: The in_alarm, fault and out_of_service bits are automatically maintained by the object. The overridden bit is a local matter. Dev must: See general input rules (for overridden if used).

  • out_of_service: Dev must: Ignore physical contact and do not set present value.

  • reliability: Dev must: Update from hardware health.

  • change_of_state_count, change_of_state_time, elapsed_active_time, time_of_state_count_reset, time_of_active_time_reset: History counters for state transitions and active time. Dev must: On every actual logical state flip (after polarity), bump count, set time, accumulate elapsed if active. On reset writes to the reset times, snapshot current and clear counters. These are side-effect "live" history properties you maintain from state changes (analog to value_* in Accumulator).

  • active_text / inactive_text: Human strings. Dev must: Set at init; changes are just data.

  • Intrinsic (alarm_values + event set): For change_of_state alarming. Dev must: Re-eval CHANGE_OF_STATE algorithm on PV changes; manage event state/notifications.

See the rest of the dev notes for polarity, write protection, side-effect counters, out_of_service, reliability, COV, intrinsic.

BinaryInput is the classic "dry contact / run status / limit switch" object.

Polarity handling: The object stores the logical state after polarity has been applied. Your contact reader must do:

raw = read_gpio_or_fieldbus()
logical = if polarity == :reverse, do: not raw, else: raw
update_property(obj, :present_value, logical)

You can also use set_input/2 to update the present value. The active_text / inactive_text are purely for human consumption (HMI, alarm messages); the wire protocol always uses the enumerated 0/1.

Write protection (same rule as every input): The server-side WriteProperty handler must reject writes to :present_value (and the counter/time fields if you treat them as read-only from the wire) whenever out_of_service == false. Local driver code is the only thing allowed to advance change_of_state_count, elapsed_active_time, change_of_state_time etc.

Side-effect counters you must maintain: Every time the logical state actually flips you should:

  • bump change_of_state_count
  • set change_of_state_time to now
  • if the new state is ACTIVE, start / accumulate elapsed_active_time
  • keep time_of_state_count_reset / time_of_active_time_reset when a client (or you) resets the counters. These are ordinary properties; you update them with the normal update_property/3 calls.

out_of_service: While true, the contact is ignored for alarming and for the counters (or you freeze them). A test tool can force ACTIVE/INACTIVE for verification of downstream logic (schedules, interlocks, etc.).

Intrinsic CHANGE_OF_STATE + optional FAULT_STATE: After you update PV or reliability, your event engine re-evaluates the algorithm that lives in the object's event fields. The object stores the current event_state, event_timestamps, acked_transitions etc.; you drive the transitions and the notification emission.

Reliability examples for a binary input:

  • :no_fault_detected
  • :communication_failure (if the contact is on a remote I/O block)
  • :process_error (welded contact detected by cross-check with a second sensor)

Use the generated property tables (bottom of the moduledoc) to see exactly which fields become active only with intrinsic_reporting: true.

Intrinsic Reporting

When intrinsic_reporting: true is passed to create/4, the object applies the CHANGE_OF_STATE algorithm and the associated alarm/event properties become active.

Examples

Creating a Binary Input with state texts:

iex> {:ok, bi} = BACnet.Protocol.ObjectTypes.BinaryInput.create(5, "Door Contact", %{active_text: "Open", inactive_text: "Closed"}); bi.active_text
"Open"

Using physical_input + intrinsic_reporting options:

iex> {:ok, bi} = BACnet.Protocol.ObjectTypes.BinaryInput.create(6, "RunStat", %{}, intrinsic_reporting: true, physical_input: true); is_boolean(bi.present_value)
true

See Also



The following part has been automatically generated.

Click to expand This module defines a BACnet object of the type `binary_input`. The following properties are defined: | Property | Revision | Required | Readonly | Protected | Intrinsic | |----------|----------|----------|----------|-----------|-----------| | acked_transitions | | | X | | X | | active_text | | | | | | | alarm_value | | | | | X | | change_of_state_count | | | | | | | change_of_state_time | | | | | | | description | | | | | | | device_type | | | | | | | elapsed_active_time | | | | | | | 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 | | inactive_text | | | | | | | limit_enable | | | | | X | | notification_class | | | | | X | | notify_type | | | | | X | | object_instance | | X | X | | | | object_name | | X | X | | | | out_of_service | | X | | | | | polarity | | X | | | | | present_value | | X | | | | | profile_location | 19 | | | | | | profile_name | | | | | | | reliability | | | | | | | reliability_evaluation_inhibit | | | | | | | status_flags | | X | X | | | | tags | 19 | | | | | | time_delay | | | | | X | | time_delay_normal | | | | | X | | time_of_active_time_reset | | | | | | | time_of_state_count_reset | | | | | | The following properties have additional semantics: | Property | Has Default | Has Init | Implicit Relationships | Validators | Annotations | |----------|-------------|----------|------------------------|------------|-------------| | active_text | X | | inactive_text | | | | alarm_value | X | | | | `encode_as: :enumerated` | | change_of_state_count | X | | | | | | change_of_state_time | X | | change_of_state_count | | | | elapsed_active_time | X | | time_of_active_time_reset | Type | | | event_algorithm_inhibit_ref | | | event_algorithm_inhibit | | | | inactive_text | X | | | | | | polarity | X | | | | | | present_value | X | | | | `encode_as: :enumerated, readonly_when: {:out_of_service, false}` | | profile_location | | | | Fun | `revision: 19` | | reliability | | | reliability_evaluation_inhibit | | | | tags | | | | | `revision: 19` | | time_of_active_time_reset | X | | | | | | time_of_state_count_reset | X | | | | | The following table shows the default values and/or init functions: | Property | Default Value | Init Function | |----------|---------------|---------------| | active_text | `"Active"` | | | alarm_value | `true` | | | change_of_state_count | `0` | | | change_of_state_time | `%BACnet.Protocol.BACnetDateTime{...}` | | | elapsed_active_time | `0` | | | inactive_text | `"Inactive"` | | | polarity | `:normal` | | | time_of_active_time_reset | `%BACnet.Protocol.BACnetDateTime{...}` | | | time_of_state_count_reset | `%BACnet.Protocol.BACnetDateTime{...}` | |

Summary

Types

Common object options for creation - all are optional.

Options accepted when creating or configuring a Binary Input object.

Available property names for this object.

The structure for property errors.

t()

Represents a Binary 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.

Sets the physical input and writes to the present value property in respect to the polarity.

Auto generated function to check whether the object type supports intrinsic reporting.

Updates a property of an object.

Types

common_object_opts()

@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 a Constants.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 type atom are 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" (usually Encoding structs).
  • 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. See BACnet.Protocol.Constants.protocol_revision/0 for 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. Value true neither type nor value are validated. Value :value means the type is still validated and only the value validator is not run (if present). The property's validator_fun will also be skipped.

object_opts()

@type object_opts() ::
  {:intrinsic_reporting, boolean()}
  | {:physical_input, boolean()}
  | common_object_opts()

Options accepted when creating or configuring a Binary Input object.

In addition to the common options, Binary Input supports:

  • intrinsic_reporting - Enables CHANGE_OF_STATE intrinsic reporting.
  • physical_input - Marks the object as directly representing a physical sensor input (affects present value / polarity handling via helper functions).

property_name()

@type property_name() ::
  :acked_transitions
  | :active_text
  | :alarm_value
  | :change_of_state_count
  | :change_of_state_time
  | :description
  | :device_type
  | :elapsed_active_time
  | :event_algorithm_inhibit
  | :event_algorithm_inhibit_ref
  | :event_detection_enable
  | :event_enable
  | :event_message_texts
  | :event_message_texts_config
  | :event_state
  | :event_timestamps
  | :inactive_text
  | :limit_enable
  | :notification_class
  | :notify_type
  | :object_instance
  | :object_name
  | :out_of_service
  | :polarity
  | :present_value
  | :profile_location
  | :profile_name
  | :reliability
  | :reliability_evaluation_inhibit
  | :status_flags
  | :tags
  | :time_delay
  | :time_delay_normal
  | :time_of_active_time_reset
  | :time_of_state_count_reset

Available property names for this object.

property_update_error()

@type property_update_error() ::
  {:error,
   {error :: atom(),
    property :: BACnet.Protocol.Constants.property_identifier()}}

The structure for property errors.

t()

@type t() :: %BACnet.Protocol.ObjectTypes.BinaryInput{
  _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,
  active_text: String.t() | nil,
  alarm_value: boolean() | nil,
  change_of_state_count: non_neg_integer() | nil,
  change_of_state_time: BACnet.Protocol.BACnetDateTime.t() | nil,
  description: String.t() | nil,
  device_type: String.t() | nil,
  elapsed_active_time: BACnet.Protocol.ApplicationTags.unsigned32() | 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,
  inactive_text: String.t() | nil,
  limit_enable: BACnet.Protocol.LimitEnable.t() | 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(),
  polarity:
    BACnet.Protocol.Constants.polarity()
    | (reserved_or_vendor_extension :: non_neg_integer()),
  present_value: boolean(),
  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,
  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,
  time_of_active_time_reset: BACnet.Protocol.BACnetDateTime.t() | nil,
  time_of_state_count_reset: BACnet.Protocol.BACnetDateTime.t() | nil
}

Represents a Binary 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.

The physical input decouples the present value and the polarity from the physical state. The present value reflects the logical state of the object. To set the logical state, call set_input/2 and the function writes to the present value in respect to the polarity. The physical input is NOT a real BACnet property.

Functions

add_property(object, property, value)

@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.

create(instance_number, object_name, properties \\ %{}, opts \\ [])

@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.

get_all_properties()

@spec get_all_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of all properties this object supports.

get_annotation(name)

@spec get_annotation(property_name()) :: [term()]

Auto generated function to get the annotations for the given property name.

get_annotations()

@spec get_annotations() :: [{name :: property_name(), values :: [term()]}]

Auto generated function to get the list of annotations for each property.

get_cov_properties()

@spec get_cov_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of properties used for COV reporting.

get_intrinsic_properties()

@spec get_intrinsic_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of intrinsic properties.

get_object_identifier(object)

@spec get_object_identifier(t()) :: BACnet.Protocol.ObjectIdentifier.t()

Get the BACnet object identifier.

get_optional_properties()

@spec get_optional_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of optional properties.

get_properties(object)

@spec get_properties(t()) :: [BACnet.Protocol.Constants.property_identifier()]

Get the list of properties the object has.

get_properties_type_map()

@spec get_properties_type_map() :: map()

Auto generated function to get a map of property name to type.

get_property(object, property)

Get a property's value from an object.

get_protected_properties()

@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.

get_readonly_properties()

@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.

get_required_properties()

@spec get_required_properties() :: [BACnet.Protocol.Constants.property_identifier()]

Auto generated function to get the names of required properties.

has_property?(object, property)

Checks if the given object has the given property.

See BACnet.Protocol.ObjectsUtility.has_property?/2 for implementation details.

intrinsic_reporting?(object)

@spec intrinsic_reporting?(t()) :: boolean()

Checks if the given object has Intrinsic Reporting enabled.

property_writable?(object, property)

@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.

remove_property(object, property)

@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.

set_input(object, value)

@spec set_input(t(), boolean()) :: {:ok, t()} | property_update_error()

Sets the physical input and writes to the present value property in respect to the polarity.

If the object is out of service, the present value won't be updated.

supports_intrinsic()

@spec supports_intrinsic() :: boolean()

Auto generated function to check whether the object type supports intrinsic reporting.

update_property(object, property, value)

@spec update_property(t(), BACnet.Protocol.Constants.property_identifier(), term()) ::
  {:ok, t()} | property_update_error()

Updates a property of an object.