Skip to content

Commit

Permalink
Merge pull request #1607 from samson0v/issues/iotgw-376
Browse files Browse the repository at this point in the history
Added Async BACnet Connector
  • Loading branch information
imbeacon authored Dec 3, 2024
2 parents 4f9a3ab + 0be4a78 commit adfa3bc
Show file tree
Hide file tree
Showing 28 changed files with 1,251 additions and 400 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2022. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2022. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
36 changes: 21 additions & 15 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,28 @@
long_description_content_type="text/markdown",
include_package_data=True,
python_requires=">=3.7",
packages=['thingsboard_gateway', 'thingsboard_gateway.gateway', 'thingsboard_gateway.gateway.proto', 'thingsboard_gateway.gateway.grpc_service',
'thingsboard_gateway.storage', 'thingsboard_gateway.storage.memory', 'thingsboard_gateway.gateway.shell', 'thingsboard_gateway.gateway.report_strategy',
'thingsboard_gateway.storage.file', 'thingsboard_gateway.storage.sqlite', 'thingsboard_gateway.gateway.entities',
'thingsboard_gateway.connectors', 'thingsboard_gateway.connectors.ble', 'thingsboard_gateway.connectors.socket',
'thingsboard_gateway.connectors.mqtt', 'thingsboard_gateway.connectors.xmpp', 'thingsboard_gateway.connectors.modbus_async',
'thingsboard_gateway.connectors.opcua', 'thingsboard_gateway.connectors.request', 'thingsboard_gateway.connectors.ocpp',
'thingsboard_gateway.connectors.modbus', 'thingsboard_gateway.connectors.can', 'thingsboard_gateway.connectors.bacnet',
'thingsboard_gateway.connectors.bacnet.bacnet_utilities', 'thingsboard_gateway.connectors.odbc',
'thingsboard_gateway.connectors.rest', 'thingsboard_gateway.connectors.snmp', 'thingsboard_gateway.connectors.ftp',
packages=['thingsboard_gateway', 'thingsboard_gateway.gateway', 'thingsboard_gateway.gateway.proto',
'thingsboard_gateway.gateway.grpc_service', 'thingsboard_gateway.storage', 'thingsboard_gateway.storage.memory',
'thingsboard_gateway.gateway.shell', 'thingsboard_gateway.gateway.report_strategy', 'thingsboard_gateway.storage.file',
'thingsboard_gateway.storage.sqlite', 'thingsboard_gateway.gateway.entities', 'thingsboard_gateway.connectors',
'thingsboard_gateway.connectors.ble', 'thingsboard_gateway.extensions.ble',
'thingsboard_gateway.connectors.socket', 'thingsboard_gateway.extensions.socket',
'thingsboard_gateway.connectors.mqtt', 'thingsboard_gateway.extensions.mqtt',
'thingsboard_gateway.connectors.xmpp', 'thingsboard_gateway.extensions.xmpp',
'thingsboard_gateway.connectors.modbus', 'thingsboard_gateway.extensions.modbus',
'thingsboard_gateway.connectors.modbus_old', 'thingsboard_gateway.connectors.modbus_old',
'thingsboard_gateway.connectors.opcua', 'thingsboard_gateway.extensions.opcua',
'thingsboard_gateway.connectors.request', 'thingsboard_gateway.extensions.request',
'thingsboard_gateway.connectors.ocpp', 'thingsboard_gateway.extensions.ocpp',
'thingsboard_gateway.connectors.can', 'thingsboard_gateway.extensions.can',
'thingsboard_gateway.connectors.bacnet_old', 'thingsboard_gateway.connectors.bacnet_old.bacnet_utilities', 'thingsboard_gateway.extensions.bacnet_old',
'thingsboard_gateway.connectors.odbc', 'thingsboard_gateway.extensions.odbc',
'thingsboard_gateway.connectors.bacnet', 'thingsboard_gateway.extensions.bacnet',
'thingsboard_gateway.connectors.rest', 'thingsboard_gateway.extensions.rest',
'thingsboard_gateway.connectors.snmp', 'thingsboard_gateway.extensions.snmp',
'thingsboard_gateway.connectors.ftp', 'thingsboard_gateway.extensions.ftp',
'thingsboard_gateway.tb_utility', 'thingsboard_gateway.extensions',
'thingsboard_gateway.extensions.mqtt', 'thingsboard_gateway.extensions.modbus', 'thingsboard_gateway.extensions.opcua',
'thingsboard_gateway.extensions.ocpp', 'thingsboard_gateway.extensions.ble',
'thingsboard_gateway.extensions.serial', 'thingsboard_gateway.extensions.request',
'thingsboard_gateway.extensions.can', 'thingsboard_gateway.extensions.bacnet', 'thingsboard_gateway.extensions.odbc',
'thingsboard_gateway.extensions.rest', 'thingsboard_gateway.extensions.snmp', 'thingsboard_gateway.extensions.ftp',
'thingsboard_gateway.extensions.socket', 'thingsboard_gateway.extensions.xmpp', 'thingsboard_gateway.gateway.statistics'
'thingsboard_gateway.extensions.serial', 'thingsboard_gateway.gateway.statistics'
],
install_requires=[
'setuptools',
Expand Down
43 changes: 28 additions & 15 deletions thingsboard_gateway/config/bacnet.json
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
{
"general": {
"application": {
"objectName": "TB_gateway",
"address": "0.0.0.0:47808",
"address": "0.0.0.0",
"objectIdentifier": 599,
"maxApduLengthAccepted": 1476,
"segmentationSupported": "segmentedBoth",
"vendorIdentifier": 15
"vendorIdentifier": 15,
"deviceDiscoveryTimeoutInSec": 5
},
"devices": [
{
"deviceName": "BACnet Device ${objectName}",
"deviceType": "default",
"address": "192.168.2.110:47808",
"deviceInfo": {
"deviceNameExpressionSource": "expression",
"deviceNameExpression": "BACnet Device ${objectName}",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"address": "192.168.1.160:49644",
"pollPeriod": 10000,
"attributes": [
{
"key": "temperature",
"type": "string",
"objectId": "analogOutput:1",
"objectType": "analogInput",
"objectId": "0",
"propertyId": "presentValue"
}
],
"timeseries": [
{
"key": "state",
"type": "bool",
"objectId": "binaryValue:1",
"key": "water_temp",
"objectType": "analogInput",
"objectId": "1",
"propertyId": "presentValue"
},
{
"key": "temp_outdoor",
"objectType": "analogInput",
"objectId": "2",
"propertyId": "presentValue"
}
],
"attributeUpdates": [
{
"key": "brightness",
"requestType": "writeProperty",
"objectId": "analogOutput:1",
"objectType": "analogInput",
"objectId": "1",
"propertyId": "presentValue"
}
],
Expand All @@ -42,14 +53,16 @@
"method": "set_state",
"requestType": "writeProperty",
"requestTimeout": 10000,
"objectId": "binaryOutput:1",
"objectType": "binaryOutput",
"objectId": "1",
"propertyId": "presentValue"
},
{
"method": "get_state",
"requestType": "readProperty",
"requestTimeout": 10000,
"objectId": "binaryOutput:1",
"objectType": "analogInput",
"objectId": "0",
"propertyId": "presentValue"
}
]
Expand Down
40 changes: 40 additions & 0 deletions thingsboard_gateway/connectors/bacnet/application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2024. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from bacpypes3.ipv4.app import NormalApplication as App
from bacpypes3.local.device import DeviceObject
from bacpypes3.pdu import Address

from thingsboard_gateway.connectors.bacnet.entities.device_object_config import DeviceObjectConfig


class Application(App):
def __init__(self, device_object_config: DeviceObjectConfig, indication_callback, logger):
self.__device_object_config = device_object_config
self.__device_object = DeviceObject(**self.__device_object_config.device_object_config)
super().__init__(self.__device_object, Address(self.__device_object_config.address))

self.__log = logger
self.__indication_callback = indication_callback

async def indication(self, apdu) -> None:
self.__log.debug(f"(indication) Received APDU: {apdu}")
await super().indication(apdu)
self.__indication_callback(apdu)

async def do_who_is(self, device_address):
devices = await self.who_is(address=Address(device_address),
timeout=self.__device_object_config.device_discovery_timeout)
if len(devices):
return devices[0]
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2024. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from copy import deepcopy


class BackwardCompatibilityAdapter:
def __init__(self, config, logger):
self.__log = logger
self.__config = deepcopy(config)

def convert(self):
try:
self.__convert_application_section()
self.__convert_device_section()
except Exception as e:
self.__log.error('Error converting old config: %s', e)

return self.__config


def __convert_application_section(self):
general_section = self.__config.pop('general', {})
self.__config['application'] = general_section

def __convert_device_section(self):
for device in self.__config.get('devices', []):
self.__convert_device_info_section(device)

for section in ('attributes', 'timeseries'):
for item_config in device.get(section, []):
self.__convert_object_id(item_config)

for attr_update_config in device.get('attributeUpdates', []):
self.__convert_object_id(attr_update_config)

for rpc_config in device.get('rpc', []):
self.__convert_object_id(rpc_config)

@staticmethod
def __convert_device_info_section(old_device_config):
device_name = old_device_config.pop('deviceName')
device_type = old_device_config.pop('deviceType', 'default')

old_device_config['deviceInfo'] = {
'deviceNameExpressionSource': 'expression',
'deviceProfileExpressionSource': 'expression',
'deviceNameExpression': device_name,
'deviceProfileExpression': device_type
}

@staticmethod
def __convert_object_id(old_item_config):
old_object_id = old_item_config.pop('objectId')
(object_type, object_id) = old_object_id.split(':')
old_item_config['objectType'] = object_type
old_item_config['objectId'] = object_id

@staticmethod
def is_old_config(config):
if config.get('general'):
return True
Loading

0 comments on commit adfa3bc

Please sign in to comment.