Read from network

To read from a BACnet device using the bacnet instance just created by the connection. You must know what you are trying to read though and some technical specificities of BACnet. Let’s have a simple look at how things work in BACnet.

To read to a point, you need to create a request that will send some message to the network (directly to a controller (unicast) or at large (broadcast) with the object and property from the object you want to read from. The BACnet standard defines a lot of different objects. All objects provides some properties that we can read from. You can refer bacpypes source code (object.py) to get some examples.

For the sake of the explanation here, we’ll take one common object : an analog value.

The object type is an analogValue. This object contains properties. Let’s have a look to bacpypes definition of an AnalogValue

@register_object_type
class AnalogValueObject(Object):
    objectType = 'analogValue'
    _object_supports_cov = True

    properties = \
        [ ReadableProperty('presentValue', Real)
        , ReadableProperty('statusFlags', StatusFlags)
        , ReadableProperty('eventState', EventState)
        , OptionalProperty('reliability', Reliability)
        , ReadableProperty('outOfService', Boolean)
        , ReadableProperty('units', EngineeringUnits)
        , OptionalProperty('minPresValue', Real)
        , OptionalProperty('maxPresValue', Real)
        , OptionalProperty('resolution', Real)
        , OptionalProperty('priorityArray', PriorityArray)
        , OptionalProperty('relinquishDefault', Real)
        , OptionalProperty('covIncrement', Real)
        , OptionalProperty('timeDelay', Unsigned)
        , OptionalProperty('notificationClass',  Unsigned)
        , OptionalProperty('highLimit', Real)
        , OptionalProperty('lowLimit', Real)
        , OptionalProperty('deadband', Real)
        , OptionalProperty('limitEnable', LimitEnable)
        , OptionalProperty('eventEnable', EventTransitionBits)
        , OptionalProperty('ackedTransitions', EventTransitionBits)
        , OptionalProperty('notifyType', NotifyType)
        , OptionalProperty('eventTimeStamps', ArrayOf(TimeStamp, 3))
        , OptionalProperty('eventMessageTexts', ArrayOf(CharacterString, 3))
        , OptionalProperty('eventMessageTextsConfig', ArrayOf(CharacterString, 3))
        , OptionalProperty('eventDetectionEnable', Boolean)
        , OptionalProperty('eventAlgorithmInhibitRef', ObjectPropertyReference)
        , OptionalProperty('eventAlgorithmInhibit', Boolean)
        , OptionalProperty('timeDelayNormal', Unsigned)
        , OptionalProperty('reliabilityEvaluationInhibit', Boolean)
        ]

Readable properties are mandatory and all BACnet device must implement AnalogValue with those properties. Optional properties, may or may not be available, depending on the choices the manufacturer made.

With BAC0, there is two different kind of requests we can use to read from the network

  • read
  • readMultiple

Read will be used to read only one (1) property. readMultiple will be used to read multiple properties. The number here is not defined. Many elements will have an impact on the number of properties you can retrieve using readMultiple. Is the device an MSTP one or a IP one. Does the device support segmentation ? Etc. Many details that could prevent you from using readMultiple with very big requests. Usually, when discovering a device points, BAC0 will use readMultiple and will use chunks of 25 properties. It’s up to you to decide how many properties you’ll read.

So, to read from BACnet. The request will contains the address of the device from which we want to read (example ‘2:5’). Then the object type (analogValue), the instance number (the object “address” or “register”… let’s pretend it’s 1) and the property from the object we want to read (typically the ‘presentValue’).

Read examples

Once you know the device you need to read from, you can use

bacnet.read('address object object_instance property')

Read property multiple can also be used

bacnet.readMultiple('address object object_instance property_1 property_2') #or
bacnet.readMultiple('address object object_instance all')

Read multiple

Using simple read is a costly way of retrieving data. If you need to read a lot of data from a controller, and this controller supports read multiple, you should use that feature.

When defining BAC0.devices, all polling requests will use readMultiple to retrive the information on the network.

There is actually two way of defining a read multiple request. The first one inherit from bacpypes console examples and is based on a string composed from a list of properties to be read on the network. This is the example I showed previously.

Recently, a more flexible way of creating those requests have been added using a dict to create the requests. The results are then provided as a dict for clarity. Because the old way just give all the result in order of the request, which can lead to some errors and is very hard to interact with on the REPL.

The request_dict must be created like this

_rpm = {'address': '303:9',
        'objects': {
            'analogInput:1094': ['objectName', 'presentValue', 'statusFlags', 'units','description'],
            'analogValue:4410': ['objectName', 'presentValue', 'statusFlags', 'units', 'description']
            }
        }

If an array index needs to be used, the following syntax can be used in the property name

# Array index 1 of propertyName
'propertyName@idx:1'

This dict must be used with the already exsiting function bacnet.readMultiple() and passed via the argument named request_dict.

bacnet.readMultiple('303:9', request_dict=_rpm)

The result will be a dict containing all the information requested.

# result of request
{
    ('analogInput', 1094): [
        ('objectName', 'DA-VP'),
        ('presentValue', 4.233697891235352),
        ('statusFlags', [0, 0, 0, 0]),
        ('units', 'pascals'),
        ('description', 'Discharge Air Velocity Pressure')
        ],
    ('analogValue', 4410): [
        ('objectName', 'SAFLOW-ABSEFFORT'),
        ('presentValue', 0.005016503389924765),
        ('statusFlags', [0, 0, 1, 0]),
        ('units', 'percent'),
        ('description', '')
        ]
}

Write to property

To write to a single property

bacnet.write('address object object_instance property value - priority')

Write to multiple properties

Write property multiple is also implemented. You will need to build a list for your requets

r = ['analogValue 1 presentValue 100','analogValue 2 presentValue 100','analogValue 3 presentValue 100 - 8','@obj_142 1 @prop_1042 True']
bacnet.writeMultiple(addr='2:5',args=r,vendor_id=842)
..note::
WARNING. See the section on Proprietary objects and properties for details about vendor_id and @obj_142.