Skip to content

Commit 4f9f664

Browse files
authored
Merge pull request #2121 from Andry925/feat/add-rpc-to-request-connector
Added RPC to Connector support
1 parent 9a48b53 commit 4f9f664

6 files changed

Lines changed: 244 additions & 84 deletions

File tree

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
'thingsboard_gateway.connectors.opcua', 'thingsboard_gateway.extensions.opcua',
5353
'thingsboard_gateway.connectors.opcua.entities',
5454
'thingsboard_gateway.connectors.request', 'thingsboard_gateway.extensions.request',
55+
'thingsboard_gateway.connectors.request.entities',
5556
'thingsboard_gateway.connectors.ocpp', 'thingsboard_gateway.extensions.ocpp',
5657
'thingsboard_gateway.connectors.can', 'thingsboard_gateway.extensions.can',
5758
'thingsboard_gateway.connectors.odbc', 'thingsboard_gateway.extensions.odbc',

thingsboard_gateway/connectors/request/entities/__init__.py

Whitespace-only changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from dataclasses import dataclass
2+
from typing import Any, Optional
3+
4+
5+
@dataclass
6+
class RpcExecutionResult:
7+
success: bool
8+
response_body: Optional[Any] = None
9+
error: Optional[str] = None
10+
status_code: Optional[int] = None
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Copyright 2026. ThingsBoard
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from enum import Enum
16+
from thingsboard_gateway.gateway.constants import (
17+
DATA_PARAMETER,
18+
DEVICE_SECTION_PARAMETER,
19+
RPC_ID_PARAMETER,
20+
RPC_PARAMS_PARAMETER,
21+
RPC_METHOD_PARAMETER,
22+
)
23+
24+
25+
class RequestRpcType(Enum):
26+
CONNECTOR = 'CONNECTOR'
27+
DEVICE = 'DEVICE'
28+
RESERVED = 'RESERVED'
29+
30+
31+
class RequestRpcRequest:
32+
def __init__(self, content: dict):
33+
self.content = self.process_rpc_request_payload(content)
34+
self.rpc_method = self.content.get(DATA_PARAMETER, {}).get(RPC_METHOD_PARAMETER)
35+
self.rpc_type = self._get_rpc_type(self.content)
36+
self.timeout = self.content.get("timeout", 5)
37+
self.params = self.content.get(DATA_PARAMETER, {}).get(RPC_PARAMS_PARAMETER)
38+
self.id = self.content.get(DATA_PARAMETER, {}).get(RPC_ID_PARAMETER)
39+
self.device_name = self.content.get(DEVICE_SECTION_PARAMETER)
40+
41+
@staticmethod
42+
def _is_connector_rpc(content: dict):
43+
try:
44+
(connector_type, rpc_method_name) = content['data'].get('method').split('_')
45+
if connector_type == "request" and content.get('device') is None:
46+
return True
47+
48+
except (ValueError, AttributeError):
49+
return False
50+
51+
@staticmethod
52+
def _is_reserved_rpc(content: dict):
53+
rpc_method_name = content["data"]["method"].lower()
54+
if rpc_method_name in ("get", "set") and content.get("device"):
55+
return True
56+
57+
return False
58+
59+
@staticmethod
60+
def process_rpc_request_payload(content: dict):
61+
normalized_content = dict(content)
62+
if normalized_content.get("data") is None:
63+
normalized_content["data"] = {
64+
"params": normalized_content["params"],
65+
"method": normalized_content["method"],
66+
"id": normalized_content["id"],
67+
}
68+
return normalized_content
69+
70+
def _is_device_rpc(self, content: dict):
71+
return bool(content.get(DEVICE_SECTION_PARAMETER)) and not self._is_reserved_rpc(content)
72+
73+
def _get_rpc_type(self, content: dict) -> RequestRpcType:
74+
if self._is_connector_rpc(content):
75+
return RequestRpcType.CONNECTOR
76+
77+
elif self._is_reserved_rpc(content):
78+
return RequestRpcType.RESERVED
79+
80+
elif self._is_device_rpc(content):
81+
return RequestRpcType.DEVICE
82+
83+
raise ValueError('Unknown RPC type for content: %r' % content)

thingsboard_gateway/connectors/request/json_request_downlink_converter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ def convert(self, config, data):
3838
result = {
3939
"url": self.__config["requestUrlExpression"].replace("${attributeKey}", quote(attribute_key))
4040
.replace("${attributeValue}", quote(str(attribute_value)))
41-
.replace("${deviceName}", quote(data["device"])),
41+
.replace("${deviceName}", quote(data.get("device", ""))),
4242
"data": self.__config["requestValueExpression"].replace("${attributeKey}", attribute_key)
4343
.replace("${attributeValue}", str(attribute_value))
44-
.replace("${deviceName}", data["device"])
44+
.replace("${deviceName}", quote(data.get("device", ""))),
4545
}
4646
else:
4747
request_id = str(data["data"]["id"])
@@ -50,10 +50,10 @@ def convert(self, config, data):
5050
result = {
5151
"url": self.__config["requestUrlExpression"].replace("${requestId}", request_id)
5252
.replace("${methodName}", method_name)
53-
.replace("${deviceName}", quote(data["device"])),
53+
.replace("${deviceName}", quote(data.get("device", ""))),
5454
"data": self.__config["requestValueExpression"].replace("${requestId}", request_id)
5555
.replace("${methodName}", method_name)
56-
.replace("${deviceName}", data["device"])
56+
.replace("${deviceName}", quote(data.get("device", ""))),
5757
}
5858

5959
result['url'] = TBUtility.replace_params_tags(result['url'], data)

0 commit comments

Comments
 (0)