Skip to content

Commit 197447d

Browse files
authored
Merge pull request #1878 from apachler/feature/bacnet-proprietary-device-support
Added support for proprietary devices
2 parents ef8310a + ef1bf3f commit 197447d

File tree

5 files changed

+118
-5
lines changed

5 files changed

+118
-5
lines changed

thingsboard_gateway/config/bacnet.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"deviceDiscoveryTimeoutInSec": 5,
1212
"networkNumber": 0,
1313
"networkNumberQuality": "configured",
14-
"devicesDiscoverPeriodSeconds": 30
14+
"devicesDiscoverPeriodSeconds": 30,
15+
"loadProprietaryDevices": false
1516
},
1617
"foreignDevice": {
1718
"address": "0.0.0.0",

thingsboard_gateway/connectors/bacnet/application.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def __get_read_access_specifications(self, object_list, vendor_id):
246246
object_id = object['objectId']
247247
if not isinstance(object_id, ObjectIdentifier):
248248
obj_str = f"{object['objectType']},{object_id}"
249-
object_id = ObjectIdentifier(obj_str)
249+
object_id = vendor_info.object_identifier(obj_str)
250250

251251
object_class = vendor_info.get_object_class(object_id[0])
252252
if object_class is None:
@@ -258,7 +258,7 @@ def __get_read_access_specifications(self, object_list, vendor_id):
258258
object['propertyId'] = {object['propertyId']}
259259

260260
for prop in object['propertyId']:
261-
property_identifier = PropertyIdentifier(prop)
261+
property_identifier = vendor_info.property_identifier(prop)
262262

263263
properties.append(property_identifier)
264264

@@ -333,12 +333,12 @@ def decode_tag_list(self, tag_list, vendor_id):
333333
result_list = []
334334

335335
for read_access_result in tag_list.listOfReadAccessResults:
336-
object_identifier = read_access_result.objectIdentifier
336+
object_identifier = vendor_info.object_identifier(read_access_result.objectIdentifier)
337337
object_class = vendor_info.get_object_class(object_identifier[0])
338338

339339
for read_access_result_element in read_access_result.listOfResults:
340340
try:
341-
property_identifier = read_access_result_element.propertyIdentifier
341+
property_identifier = vendor_info.property_identifier(read_access_result_element.propertyIdentifier)
342342
property_array_index = read_access_result_element.propertyArrayIndex
343343
read_result = read_access_result_element.readResult
344344

thingsboard_gateway/connectors/bacnet/bacnet_connector.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from thingsboard_gateway.gateway.statistics.statistics_service import StatisticsService
4141
from thingsboard_gateway.tb_utility.tb_logger import init_logger
4242
from thingsboard_gateway.tb_utility.tb_utility import TBUtility
43+
from thingsboard_gateway.tb_utility.tb_loader import TBModuleLoader
4344

4445
try:
4546
from bacpypes3.apdu import ErrorRejectAbortNack
@@ -85,6 +86,10 @@ def __init__(self, gateway, config, connector_type):
8586
self.__parse_ede_config()
8687
self.__log.debug('EDE config parsed')
8788

89+
# importing the proprietary package registers all available custom object types and properties
90+
if self.__config.get('loadProprietaryDevices', False):
91+
TBModuleLoader.import_package_files(connector_type, "proprietary")
92+
8893
if BackwardCompatibilityAdapter.is_old_config(config):
8994
backward_compatibility_adapter = BackwardCompatibilityAdapter(config, self.__log)
9095
self.__config = backward_compatibility_adapter.convert()
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
# Siemens Desigo CC
3+
# Register proprietary Objects and Properties
4+
"""
5+
6+
from bacpypes3.constructeddata import ArrayOf
7+
from bacpypes3.vendor import VendorInfo
8+
from bacpypes3.basetypes import PropertyIdentifier
9+
from bacpypes3.object import AnalogInputObject as _AnalogInputObject
10+
from bacpypes3.object import PulseConverterObject as _PulseConverterObject
11+
from bacpypes3.object import AnalogValueObject as _AnalogValueObject
12+
from bacpypes3.object import DeviceObject as _DeviceObject
13+
from bacpypes3.object import NetworkPortObject as _NetworkPortObject
14+
from bacpypes3.primitivedata import (
15+
ObjectType,
16+
CharacterString,
17+
)
18+
19+
20+
# this vendor identifier reference is used when registering custom classes
21+
_vendor_id = 7
22+
_vendor_name = "Siemens Building Technologies"
23+
24+
25+
class ProprietaryObjectType(ObjectType):
26+
"""
27+
This is a list of the object type enumerations for proprietary object types,
28+
see Clause 23.4.1.
29+
"""
30+
31+
pass
32+
33+
34+
class ProprietaryPropertyIdentifier(PropertyIdentifier):
35+
"""
36+
This is a list of the property identifiers that are used in custom object
37+
types or are used in custom properties of standard types.
38+
"""
39+
40+
descriptionList = 3121
41+
42+
43+
# create a VendorInfo object for this custom application before registering
44+
# specialize object classes
45+
_desigo_cc = VendorInfo(
46+
_vendor_id, ProprietaryObjectType, ProprietaryPropertyIdentifier
47+
)
48+
49+
50+
class DesigoCCDeviceObject(_DeviceObject):
51+
"""
52+
When running as an instance of this custom device, the DeviceObject is
53+
an extension of the one defined in bacpypes3.device
54+
"""
55+
56+
descriptionList: CharacterString
57+
58+
59+
class NetworkPortObject(_NetworkPortObject):
60+
"""
61+
When running as an instance of this custom device, the NetworkPortObject is
62+
an extension of the one defined in bacpypes3.networkport (in this
63+
case doesn't add any proprietary properties).
64+
"""
65+
66+
pass
67+
68+
69+
class DesigoCCAnalogInputObject(_AnalogInputObject):
70+
descriptionList: ArrayOf(CharacterString)
71+
72+
73+
class DesigoCCPulseConverterObject(_PulseConverterObject):
74+
descriptionList: ArrayOf(CharacterString)
75+
76+
77+
class DesigoCCAnalogValueObject(_AnalogValueObject):
78+
descriptionList: ArrayOf(CharacterString)

thingsboard_gateway/tb_utility/tb_loader.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#
1515

1616
from importlib.util import module_from_spec, spec_from_file_location
17+
from importlib import import_module
1718
from inspect import getmembers, isclass
1819
from logging import getLogger, setLoggerClass
1920
from os import listdir, path
@@ -81,3 +82,31 @@ def import_module(extension_type, module_name):
8182
log.error("Error while importing module %s from %s.", module_name, current_extension_path, exc_info=e)
8283
errors.append(e)
8384
return errors
85+
86+
@staticmethod
87+
def import_package_files(extension_type, package_name):
88+
errors = []
89+
if len(TBModuleLoader.PATHS) == 0:
90+
TBModuleLoader.find_paths()
91+
try:
92+
for current_path in TBModuleLoader.PATHS:
93+
current_extension_path = current_path + path.sep + extension_type
94+
package_path = current_extension_path + path.sep + package_name
95+
if path.exists(package_path):
96+
for file in listdir(package_path):
97+
if not file.startswith("__") and (file.endswith(".py") or file.endswith(".pyc")):
98+
try:
99+
module_name, _ = path.splitext(file)
100+
module = f'{package_path.replace("/", ".")}.{module_name}'
101+
if module.startswith("."):
102+
module = module[1:]
103+
log.info("Import %s from %s.", module, package_path)
104+
import_module(module)
105+
except ImportError as e:
106+
log.info(e.msg)
107+
errors.append(e.msg)
108+
continue
109+
except Exception as e:
110+
log.error("Error while importing package %s from %s.", package_name, package_path, exc_info=e)
111+
errors.append(e)
112+
return errors

0 commit comments

Comments
 (0)