thingsboard-aplcache

Implementation of RPC call action

9/17/2017 8:26:12 PM

Details

diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 6fe4f6d..f45028a 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -44,6 +44,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
 import org.thingsboard.server.dao.device.DeviceService;
 import org.thingsboard.server.dao.event.EventService;
 import org.thingsboard.server.dao.plugin.PluginService;
+import org.thingsboard.server.dao.relation.RelationService;
 import org.thingsboard.server.dao.rule.RuleService;
 import org.thingsboard.server.dao.tenant.TenantService;
 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -110,6 +111,9 @@ public class ActorSystemContext {
     @Getter private AlarmService alarmService;
 
     @Autowired
+    @Getter private RelationService relationService;
+
+    @Autowired
     @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint;
 
     @Value("${actors.session.sync.timeout}")
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
index 0e2ffcb..8f65fc6 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/PluginProcessingContext.java
@@ -33,6 +33,8 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
 import org.thingsboard.server.common.data.kv.TsKvQuery;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.common.data.plugin.PluginMetaData;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 import org.thingsboard.server.common.data.rule.RuleMetaData;
 import org.thingsboard.server.common.msg.cluster.ServerAddress;
 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
@@ -395,6 +397,16 @@ public final class PluginProcessingContext implements PluginContext {
     }
 
     @Override
+    public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) {
+        return this.pluginCtx.relationService.findByFromAndType(from, relationType, RelationTypeGroup.COMMON);
+    }
+
+    @Override
+    public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId from, String relationType) {
+        return this.pluginCtx.relationService.findByToAndType(from, relationType, RelationTypeGroup.COMMON);
+    }
+
+    @Override
     public Optional<ServerAddress> resolve(EntityId entityId) {
         return pluginCtx.routingService.resolveById(entityId);
     }
diff --git a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java b/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
index 43ddce6..de859a2 100644
--- a/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/plugin/SharedPluginProcessingContext.java
@@ -30,6 +30,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
 import org.thingsboard.server.dao.customer.CustomerService;
 import org.thingsboard.server.dao.device.DeviceService;
 import org.thingsboard.server.dao.plugin.PluginService;
+import org.thingsboard.server.dao.relation.RelationService;
 import org.thingsboard.server.dao.rule.RuleService;
 import org.thingsboard.server.dao.tenant.TenantService;
 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -61,6 +62,7 @@ public final class SharedPluginProcessingContext {
     final AttributesService attributesService;
     final ClusterRpcService rpcService;
     final ClusterRoutingService routingService;
+    final RelationService relationService;
     final PluginId pluginId;
     final TenantId tenantId;
 
@@ -83,6 +85,7 @@ public final class SharedPluginProcessingContext {
         this.pluginService = sysContext.getPluginService();
         this.customerService = sysContext.getCustomerService();
         this.tenantService = sysContext.getTenantService();
+        this.relationService = sysContext.getRelationService();
     }
 
     public PluginId getPluginId() {
diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java
index 2d346e7..c825594 100644
--- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java
+++ b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/plugins/PluginContext.java
@@ -15,11 +15,14 @@
  */
 package org.thingsboard.server.extensions.api.plugins;
 
+import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
 import org.thingsboard.server.common.data.kv.TsKvEntry;
 import org.thingsboard.server.common.data.kv.TsKvQuery;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 import org.thingsboard.server.common.msg.cluster.ServerAddress;
 import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg;
 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
@@ -109,4 +112,12 @@ public interface PluginContext {
 
     void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback<List<Device>> callback);
 
+
+    /*
+    *   Relations API
+    * */
+
+    ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType);
+
+    ListenableFuture<List<EntityRelation>> findByToAndType(EntityId from, String relationType);
 }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallAction.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallAction.java
index 679c87b..5279eb1 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallAction.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallAction.java
@@ -1,3 +1,18 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * 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.
+ */
 package org.thingsboard.server.extensions.core.action.rpc;
 
 import lombok.extern.slf4j.Slf4j;
@@ -28,7 +43,8 @@ public class ServerSideRpcCallAction extends SimpleRuleLifecycleComponent implem
 
     private ServerSideRpcCallActionConfiguration configuration;
     private Optional<Template> deviceIdTemplate;
-    private Optional<Template> deviceRelationTemplate;
+    private Optional<Template> fromDeviceRelationTemplate;
+    private Optional<Template> toDeviceRelationTemplate;
     private Optional<Template> rpcCallMethodTemplate;
     private Optional<Template> rpcCallBodyTemplate;
 
@@ -37,7 +53,8 @@ public class ServerSideRpcCallAction extends SimpleRuleLifecycleComponent implem
         this.configuration = configuration;
         try {
             deviceIdTemplate = toTemplate(configuration.getDeviceIdTemplate(), "Device Id Template");
-            deviceRelationTemplate = toTemplate(configuration.getDeviceRelationTemplate(), "Device Relation Template");
+            fromDeviceRelationTemplate = toTemplate(configuration.getFromDeviceRelationTemplate(), "From Device Relation Template");
+            toDeviceRelationTemplate = toTemplate(configuration.getToDeviceRelationTemplate(), "To Device Relation Template");
             rpcCallMethodTemplate = toTemplate(configuration.getRpcCallMethodTemplate(), "RPC Call Method Template");
             rpcCallBodyTemplate = toTemplate(configuration.getRpcCallBodyTemplate(), "RPC Call Body Template");
         } catch (ParseException e) {
@@ -55,7 +72,8 @@ public class ServerSideRpcCallAction extends SimpleRuleLifecycleComponent implem
             ServerSideRpcCallActionMsg.ServerSideRpcCallActionMsgBuilder builder = ServerSideRpcCallActionMsg.builder();
 
             deviceIdTemplate.ifPresent(t -> builder.deviceId(VelocityUtils.merge(t, context)));
-            deviceRelationTemplate.ifPresent(t -> builder.deviceRelation(VelocityUtils.merge(t, context)));
+            fromDeviceRelationTemplate.ifPresent(t -> builder.fromDeviceRelation(VelocityUtils.merge(t, context)));
+            toDeviceRelationTemplate.ifPresent(t -> builder.toDeviceRelation(VelocityUtils.merge(t, context)));
             rpcCallMethodTemplate.ifPresent(t -> builder.rpcCallMethod(VelocityUtils.merge(t, context)));
             rpcCallBodyTemplate.ifPresent(t -> builder.rpcCallBody(VelocityUtils.merge(t, context)));
             return Optional.of(new ServerSideRpcCallRuleToPluginActionMsg(toDeviceActorMsg.getTenantId(), toDeviceActorMsg.getCustomerId(), toDeviceActorMsg.getDeviceId(),
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionConfiguration.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionConfiguration.java
index 30040ed..49f217e 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionConfiguration.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionConfiguration.java
@@ -26,7 +26,9 @@ public class ServerSideRpcCallActionConfiguration {
     private String sendFlag;
 
     private String deviceIdTemplate;
-    private String deviceRelationTemplate;
     private String rpcCallMethodTemplate;
     private String rpcCallBodyTemplate;
+    private long rpcCallTimeoutInSec;
+    private String fromDeviceRelationTemplate;
+    private String toDeviceRelationTemplate;
 }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionMsg.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionMsg.java
index 6dcf217..f11e12b 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionMsg.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallActionMsg.java
@@ -1,3 +1,18 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * 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.
+ */
 package org.thingsboard.server.extensions.core.action.rpc;
 
 import lombok.Builder;
@@ -13,8 +28,11 @@ import java.io.Serializable;
 public class ServerSideRpcCallActionMsg implements Serializable {
 
     private String deviceId;
-    private String deviceRelation;
     private String rpcCallMethod;
     private String rpcCallBody;
+    private long rpcCallTimeoutInSec;
+
+    private String fromDeviceRelation;
+    private String toDeviceRelation;
 
 }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallRuleToPluginActionMsg.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallRuleToPluginActionMsg.java
index 0f1e916..bc248e5 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallRuleToPluginActionMsg.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/rpc/ServerSideRpcCallRuleToPluginActionMsg.java
@@ -1,3 +1,18 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * 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.
+ */
 package org.thingsboard.server.extensions.core.action.rpc;
 
 import org.thingsboard.server.common.data.id.CustomerId;
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/handlers/RpcRuleMsgHandler.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/handlers/RpcRuleMsgHandler.java
index f4e03ea..94647e4 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/handlers/RpcRuleMsgHandler.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/handlers/RpcRuleMsgHandler.java
@@ -1,31 +1,102 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * 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.
+ */
 package org.thingsboard.server.extensions.core.plugin.rpc.handlers;
 
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.RuleId;
 import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.extensions.api.plugins.PluginCallback;
 import org.thingsboard.server.extensions.api.plugins.PluginContext;
 import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
 import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
 import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;
+import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody;
 import org.thingsboard.server.extensions.api.rules.RuleException;
 import org.thingsboard.server.extensions.core.action.rpc.ServerSideRpcCallActionMsg;
+import org.thingsboard.server.extensions.core.action.rpc.ServerSideRpcCallRuleToPluginActionMsg;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * Created by ashvayka on 14.09.17.
  */
+@Slf4j
 public class RpcRuleMsgHandler implements RuleMsgHandler {
 
     @Override
     public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException {
-        if (msg instanceof ServerSideRpcCallActionMsg) {
-            handle(ctx, tenantId, ruleId, (ServerSideRpcCallActionMsg) msg);
+        if (msg instanceof ServerSideRpcCallRuleToPluginActionMsg) {
+            handle(ctx, tenantId, ruleId, ((ServerSideRpcCallRuleToPluginActionMsg) msg).getPayload());
         } else {
             throw new RuntimeException("Not supported msg: " + msg + "!");
         }
     }
 
-    private void handle(PluginContext ctx, TenantId tenantId, RuleId ruleId, ServerSideRpcCallActionMsg msg) {
-//        TODO: implement
-//        ToDeviceRpcRequest request = new ToDeviceRpcRequest();
-//        ctx.sendRpcRequest(request);
+    private void handle(final PluginContext ctx, TenantId tenantId, RuleId ruleId, ServerSideRpcCallActionMsg msg) {
+        DeviceId deviceId = new DeviceId(UUID.fromString(msg.getDeviceId()));
+        ctx.checkAccess(deviceId, new PluginCallback<Void>() {
+            @Override
+            public void onSuccess(PluginContext dummy, Void value) {
+                try {
+                    List<EntityId> deviceIds;
+                    if (StringUtils.isEmpty(msg.getFromDeviceRelation()) && StringUtils.isEmpty(msg.getToDeviceRelation())) {
+                        deviceIds = Collections.singletonList(deviceId);
+                    } else if (!StringUtils.isEmpty(msg.getFromDeviceRelation())) {
+                        List<EntityRelation> relations = ctx.findByFromAndType(deviceId, msg.getFromDeviceRelation()).get();
+                        deviceIds = relations.stream().map(EntityRelation::getTo).collect(Collectors.toList());
+                    } else {
+                        List<EntityRelation> relations = ctx.findByToAndType(deviceId, msg.getFromDeviceRelation()).get();
+                        deviceIds = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toList());
+                    }
+                    ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(msg.getRpcCallMethod(), msg.getRpcCallBody());
+                    long expirationTime = System.currentTimeMillis() + msg.getRpcCallTimeoutInSec();
+                    for (EntityId address : deviceIds) {
+                        DeviceId tmpId = new DeviceId(address.getId());
+                        ctx.checkAccess(tmpId, new PluginCallback<Void>() {
+                            @Override
+                            public void onSuccess(PluginContext ctx, Void value) {
+                                ctx.sendRpcRequest(new ToDeviceRpcRequest(UUID.randomUUID(),
+                                        tenantId, tmpId, true, expirationTime, body)
+                                );
+                                log.trace("[{}] Sent RPC Call Action msg", tmpId);
+                            }
+
+                            @Override
+                            public void onFailure(PluginContext ctx, Exception e) {
+                                log.info("[{}] Failed to process RPC Call Action msg", tmpId, e);
+                            }
+                        });
+                    }
+                } catch (Exception e) {
+                    log.info("Failed to process RPC Call Action msg", e);
+                }
+            }
+
+            @Override
+            public void onFailure(PluginContext dummy, Exception e) {
+                log.info("[{}] Failed to process RPC Call Action msg", deviceId, e);
+            }
+        });
     }
 }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/RpcPlugin.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/RpcPlugin.java
index b24e4a3..00ab496 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/RpcPlugin.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/rpc/RpcPlugin.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2017 The Thingsboard Authors
- * <p>
+ *
  * 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
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
+ *
+ *     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.
@@ -26,6 +26,7 @@ import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
 import org.thingsboard.server.extensions.core.action.rpc.ServerSideRpcCallAction;
 import org.thingsboard.server.extensions.core.plugin.rpc.handlers.RpcRestMsgHandler;
+import org.thingsboard.server.extensions.core.plugin.rpc.handlers.RpcRuleMsgHandler;
 
 /**
  * @author Andrew Shvayka
@@ -65,7 +66,7 @@ public class RpcPlugin extends AbstractPlugin<RpcPluginConfiguration> {
 
     @Override
     protected RuleMsgHandler getRuleMsgHandler() {
-        return new DefaultRuleMsgHandler();
+        return new RpcRuleMsgHandler();
     }
 
     @Override
diff --git a/extensions-core/src/main/resources/SendMailActionDescriptor.json b/extensions-core/src/main/resources/SendMailActionDescriptor.json
index ed8cdf4..f1df258 100644
--- a/extensions-core/src/main/resources/SendMailActionDescriptor.json
+++ b/extensions-core/src/main/resources/SendMailActionDescriptor.json
@@ -4,7 +4,7 @@
     "type": "object",
     "properties": {
       "sendFlag": {
-        "title": "Send flag",
+        "title": "Send flag (empty or 'isNewAlarm', 'isExistingAlarm', 'isClearedAlarm', 'isNewOrClearedAlarm')",
         "type": "string"
       },
       "fromTemplate": {
diff --git a/extensions-core/src/main/resources/ServerSideRpcCallActionDescriptor.json b/extensions-core/src/main/resources/ServerSideRpcCallActionDescriptor.json
index e69c38e..2f814ed 100644
--- a/extensions-core/src/main/resources/ServerSideRpcCallActionDescriptor.json
+++ b/extensions-core/src/main/resources/ServerSideRpcCallActionDescriptor.json
@@ -4,16 +4,13 @@
     "type": "object",
     "properties": {
       "sendFlag": {
-        "title": "Send flag",
+        "title": "Send flag (empty or 'isNewAlarm', 'isExistingAlarm', 'isClearedAlarm', 'isNewOrClearedAlarm')",
         "type": "string"
       },
       "deviceIdTemplate": {
         "title": "Device ID template",
-        "type": "string"
-      },
-      "deviceRelationTemplate": {
-        "title": "Device Relation template",
-        "type": "string"
+        "type": "string",
+        "default": "$deviceId"
       },
       "rpcCallMethodTemplate": {
         "title": "RPC Call template",
@@ -22,24 +19,39 @@
       "rpcCallBodyTemplate": {
         "title": "RPC Call Body template",
         "type": "string"
+      },
+      "rpcCallTimeoutInSec": {
+        "title": "RPC Call timeout in seconds",
+        "type": "integer",
+        "default": 60
+      },
+      "fromDeviceRelationTemplate": {
+        "title": "From Device Relation template",
+        "type": "string"
+      },
+      "toDeviceRelationTemplate": {
+        "title": "To Device Relation template",
+        "type": "string"
       }
     },
     "required": [
       "deviceIdTemplate",
-      "deviceRelationTemplate",
       "rpcCallMethodTemplate",
-      "rpcCallBodyTemplate"
+      "rpcCallBodyTemplate",
+      "rpcCallTimeoutInSec"
     ]
   },
   "form": [
     "sendFlag",
     "deviceIdTemplate",
-    "deviceRelationTemplate",
     "rpcCallMethodTemplate",
     {
       "key": "rpcCallBodyTemplate",
       "type": "textarea",
       "rows": 5
-    }
+    },
+    "rpcCallTimeoutInSec",
+    "fromDeviceRelationTemplate",
+    "toDeviceRelationTemplate"
   ]
 }
\ No newline at end of file