Commit 90c84f84d0a8e56523fca9bc7bd8a931340fdaaf

Authored by Vladyslav_Prykhodko
Committed by Andrew Shvayka
1 parent b0c6ce9f

UI: Add control widget support persistent command (Add settings: persistent and …

…persistentPollingInterval)
... ... @@ -18,8 +18,8 @@
18 18 "resources": [],
19 19 "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>",
20 20 "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n",
21   - "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}",
22   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\"\n ]\n}",
  21 + "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}",
  22 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
23 23 "dataKeySettingsSchema": "{}\n",
24 24 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
25 25 }
... ... @@ -55,7 +55,7 @@
55 55 "templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>",
56 56 "templateCss": "",
57 57 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
58   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}",
  58 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
59 59 "dataKeySettingsSchema": "{}\n",
60 60 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
61 61 }
... ... @@ -73,7 +73,7 @@
73 73 "templateHtml": "<tb-switch [ctx]='ctx'></tb-switch>",
74 74 "templateCss": "",
75 75 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
76   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
  76 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
77 77 "dataKeySettingsSchema": "{}\n",
78 78 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
79 79 }
... ... @@ -91,7 +91,7 @@
91 91 "templateHtml": "<tb-round-switch [ctx]='ctx'></tb-round-switch>",
92 92 "templateCss": "",
93 93 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
94   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
  94 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
95 95 "dataKeySettingsSchema": "{}\n",
96 96 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
97 97 }
... ... @@ -109,7 +109,7 @@
109 109 "templateHtml": "<tb-led-indicator [ctx]='ctx'></tb-led-indicator>",
110 110 "templateCss": "",
111 111 "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
112   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
  112 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
113 113 "dataKeySettingsSchema": "{}\n",
114 114 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
115 115 }
... ... @@ -126,8 +126,8 @@
126 126 "resources": [],
127 127 "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\n <div fxFlex=\"20\" class=\"title-container\" fxLayout=\"row\"\n fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '10px'}\"\n class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\n <div>\n <button mat-button (click)=\"sendCommand()\"\n [class.mat-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [ngStyle]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\n fxLayout=\"row\" fxLayoutAlign=\"center center\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
128 128 "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
129   - "controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n",
130   - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"title\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"buttonText\": {\n \"title\": \"Button label\",\n \"type\": \"string\",\n \"default\": \"Send RPC\"\n },\n \"oneWayElseTwoWay\": {\n \"title\": \"Is One Way Command\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showError\": {\n \"title\": \"Show RPC command execution error\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"methodName\": {\n \"title\": \"RPC method\",\n \"type\": \"string\",\n \"default\": \"rpcCommand\"\n },\n \"methodParams\": {\n \"title\": \"RPC method params\",\n \"type\": \"string\",\n \"default\": \"{}\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 5000\n },\n \"styleButton\": {\n \"type\": \"object\",\n \"title\": \"Button Style\",\n \"properties\": {\n \"isRaised\": {\n \"type\": \"boolean\",\n \"title\": \"Raised\",\n \"default\": true\n },\n \"isPrimary\": {\n \"type\": \"boolean\",\n \"title\": \"Primary color\",\n \"default\": false\n },\n \"bgColor\": {\n \"type\": \"string\",\n \"title\": \"Button background color\",\n \"default\": null\n },\n \"textColor\": {\n \"type\": \"string\",\n \"title\": \"Button text color\",\n \"default\": null\n }\n }\n },\n \"required\": []\n }\n },\n \"form\": [\n \"title\",\n \"buttonText\",\n \"oneWayElseTwoWay\",\n \"showError\",\n \"methodName\",\n {\n \"key\": \"methodParams\",\n \"type\": \"json\"\n },\n \"requestTimeout\",\n {\n \"key\": \"styleButton\",\n \"items\": [\n \"styleButton.isRaised\",\n \"styleButton.isPrimary\",\n {\n \"key\": \"styleButton.bgColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"styleButton.textColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n\n}",
  129 + "controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n",
  130 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"title\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"buttonText\": {\n \"title\": \"Button label\",\n \"type\": \"string\",\n \"default\": \"Send RPC\"\n },\n \"oneWayElseTwoWay\": {\n \"title\": \"Is One Way Command\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showError\": {\n \"title\": \"Show RPC command execution error\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"methodName\": {\n \"title\": \"RPC method\",\n \"type\": \"string\",\n \"default\": \"rpcCommand\"\n },\n \"methodParams\": {\n \"title\": \"RPC method params\",\n \"type\": \"string\",\n \"default\": \"{}\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 5000\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n },\n \"styleButton\": {\n \"type\": \"object\",\n \"title\": \"Button Style\",\n \"properties\": {\n \"isRaised\": {\n \"type\": \"boolean\",\n \"title\": \"Raised\",\n \"default\": true\n },\n \"isPrimary\": {\n \"type\": \"boolean\",\n \"title\": \"Primary color\",\n \"default\": false\n },\n \"bgColor\": {\n \"type\": \"string\",\n \"title\": \"Button background color\",\n \"default\": null\n },\n \"textColor\": {\n \"type\": \"string\",\n \"title\": \"Button text color\",\n \"default\": null\n }\n }\n },\n \"required\": []\n }\n },\n \"form\": [\n \"title\",\n \"buttonText\",\n \"oneWayElseTwoWay\",\n \"showError\",\n \"methodName\",\n {\n \"key\": \"methodParams\",\n \"type\": \"json\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n },\n {\n \"key\": \"styleButton\",\n \"items\": [\n \"styleButton.isRaised\",\n \"styleButton.isPrimary\",\n {\n \"key\": \"styleButton.bgColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"styleButton.textColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n\n}",
131 131 "dataKeySettingsSchema": "{}\n",
132 132 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"oneWayElseTwoWay\":true,\"buttonText\":\"Send RPC\",\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"methodName\":\"rpcCommand\",\"methodParams\":\"{}\"},\"title\":\"RPC Button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
133 133 }
... ...
... ... @@ -69,11 +69,27 @@ public class RpcV2Controller extends AbstractRpcController {
69 69 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
70 70 @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
71 71 @ResponseBody
72   - public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  72 + public ResponseEntity<Rpc> getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
73 73 checkParameter("RpcId", strRpc);
74 74 try {
75 75 RpcId rpcId = new RpcId(UUID.fromString(strRpc));
76   - return checkRpcId(rpcId, Operation.READ);
  76 + Rpc rpc = checkRpcId(rpcId, Operation.READ);
  77 + HttpStatus status;
  78 + switch (rpc.getStatus()) {
  79 + case FAILED:
  80 + status = HttpStatus.BAD_GATEWAY;
  81 + break;
  82 + case TIMEOUT:
  83 + status = HttpStatus.GATEWAY_TIMEOUT;
  84 + break;
  85 + case QUEUED:
  86 + case DELIVERED:
  87 + status = HttpStatus.ACCEPTED;
  88 + break;
  89 + default:
  90 + status = HttpStatus.OK;
  91 + }
  92 + return new ResponseEntity<>(rpc, status);
77 93 } catch (Exception e) {
78 94 throw handleException(e);
79 95 }
... ...
... ... @@ -69,8 +69,11 @@ export interface WidgetSubscriptionApi {
69 69 }
70 70
71 71 export interface RpcApi {
72   - sendOneWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean, requestUUID?: string) => Observable<any>;
73   - sendTwoWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean, requestUUID?: string) => Observable<any>;
  72 + sendOneWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean,
  73 + persistentPollingInterval?: number, requestUUID?: string) => Observable<any>;
  74 + sendTwoWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean,
  75 + persistentPollingInterval?: number, requestUUID?: string) => Observable<any>;
  76 + completedCommand: () => void;
74 77 }
75 78
76 79 export interface IWidgetUtils {
... ... @@ -301,8 +304,10 @@ export interface IWidgetSubscription {
301 304 onResetTimewindow(): void;
302 305 updateTimewindowConfig(newTimewindow: Timewindow): void;
303 306
304   - sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, requestUUID?: string): Observable<any>;
305   - sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, requestUUID?: string): Observable<any>;
  307 + sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
  308 + persistentPollingInterval?: number, requestUUID?: string): Observable<any>;
  309 + sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
  310 + persistentPollingInterval?: number, requestUUID?: string): Observable<any>;
306 311 clearRpcError(): void;
307 312
308 313 subscribe(): void;
... ...
... ... @@ -35,7 +35,7 @@ import {
35 35 LegendKeyData,
36 36 widgetType
37 37 } from '@app/shared/models/widget.models';
38   -import { HttpErrorResponse } from '@angular/common/http';
  38 +import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
39 39 import {
40 40 calculateIntervalStartEndTime,
41 41 calculateTsOffset,
... ... @@ -49,7 +49,7 @@ import {
49 49 toHistoryTimewindow,
50 50 WidgetTimewindow
51 51 } from '@app/shared/models/time/time.models';
52   -import { forkJoin, Observable, of, ReplaySubject, Subject, throwError } from 'rxjs';
  52 +import { forkJoin, Observable, of, ReplaySubject, Subject, throwError, timer } from 'rxjs';
53 53 import { CancelAnimationFrame } from '@core/services/raf.service';
54 54 import { EntityType } from '@shared/models/entity-type.models';
55 55 import { createLabelFromDatasource, deepClone, isDefined, isDefinedAndNotNull, isEqual } from '@core/utils';
... ... @@ -67,8 +67,9 @@ import {
67 67 KeyFilter,
68 68 updateDatasourceFromEntityInfo
69 69 } from '@shared/models/query/query.models';
70   -import { map } from 'rxjs/operators';
  70 +import { filter, map, switchMap, takeUntil } from 'rxjs/operators';
71 71 import { AlarmDataListener } from '@core/api/alarm-data.service';
  72 +import { PersistentRpc } from '@shared/models/rpc.models';
72 73
73 74 const moment = moment_;
74 75
... ... @@ -644,12 +645,14 @@ export class WidgetSubscription implements IWidgetSubscription {
644 645 }
645 646 }
646 647
647   - sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, requestUUID?: string): Observable<any> {
648   - return this.sendCommand(true, method, params, timeout, persistent, requestUUID);
  648 + sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
  649 + persistentPollingInterval?: number, requestUUID?: string): Observable<any> {
  650 + return this.sendCommand(true, method, params, timeout, persistent, persistentPollingInterval, requestUUID);
649 651 }
650 652
651   - sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean, requestUUID?: string): Observable<any> {
652   - return this.sendCommand(false, method, params, timeout, persistent, requestUUID);
  653 + sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
  654 + persistentPollingInterval?: number, requestUUID?: string): Observable<any> {
  655 + return this.sendCommand(false, method, params, timeout, persistent, persistentPollingInterval, requestUUID);
653 656 }
654 657
655 658 clearRpcError(): void {
... ... @@ -658,8 +661,15 @@ export class WidgetSubscription implements IWidgetSubscription {
658 661 this.callbacks.onRpcErrorCleared(this);
659 662 }
660 663
661   - sendCommand(oneWayElseTwoWay: boolean, method: string, params?: any,
662   - timeout?: number, persistent?: boolean, requestUUID?: string): Observable<any> {
  664 + completedCommand(): void {
  665 + this.executingSubjects.forEach(subject => {
  666 + subject.next();
  667 + subject.complete();
  668 + });
  669 + }
  670 +
  671 + sendCommand(oneWayElseTwoWay: boolean, method: string, params?: any, timeout?: number,
  672 + persistent?: boolean, persistentPollingInterval?: number, requestUUID?: string): Observable<any> {
663 673 if (!this.rpcEnabled) {
664 674 return throwError(new Error('Rpc disabled!'));
665 675 } else {
... ... @@ -677,7 +687,7 @@ export class WidgetSubscription implements IWidgetSubscription {
677 687 if (timeout && timeout > 0) {
678 688 requestBody.timeout = timeout;
679 689 }
680   - const rpcSubject: Subject<any> = new ReplaySubject<any>();
  690 + const rpcSubject: Subject<any> = new Subject<any>();
681 691 this.executingRpcRequest = true;
682 692 this.callbacks.rpcStateChanged(this);
683 693 if (this.ctx.utils.widgetEditMode) {
... ... @@ -695,7 +705,19 @@ export class WidgetSubscription implements IWidgetSubscription {
695 705 } else {
696 706 this.executingSubjects.push(rpcSubject);
697 707 (oneWayElseTwoWay ? this.ctx.deviceService.sendOneWayRpcCommand(this.targetDeviceId, requestBody) :
698   - this.ctx.deviceService.sendTwoWayRpcCommand(this.targetDeviceId, requestBody))
  708 + this.ctx.deviceService.sendTwoWayRpcCommand(this.targetDeviceId, requestBody)).pipe(
  709 + switchMap((response) => {
  710 + if (persistent && persistentPollingInterval > 0) {
  711 + return timer(persistentPollingInterval / 2, persistentPollingInterval).pipe(
  712 + switchMap(() => this.ctx.deviceService.getPersistedRpc(response.rpcId, true)),
  713 + filter((persistentResponse: HttpResponse<PersistentRpc>) => persistentResponse.status !== 202),
  714 + map(persistentResponse => persistentResponse.body.response),
  715 + takeUntil(rpcSubject)
  716 + );
  717 + }
  718 + return of(response);
  719 + })
  720 + )
699 721 .subscribe((responseBody) => {
700 722 this.rpcRejection = null;
701 723 this.rpcErrorText = null;
... ...
... ... @@ -17,7 +17,7 @@
17 17 import { Injectable } from '@angular/core';
18 18 import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
19 19 import { Observable, ReplaySubject } from 'rxjs';
20   -import { HttpClient } from '@angular/common/http';
  20 +import { HttpClient, HttpResponse } from '@angular/common/http';
21 21 import { PageLink } from '@shared/models/page/page-link';
22 22 import { PageData } from '@shared/models/page/page-data';
23 23 import {
... ... @@ -30,6 +30,7 @@ import {
30 30 } from '@app/shared/models/device.models';
31 31 import { EntitySubtype } from '@app/shared/models/entity-type.models';
32 32 import { AuthService } from '@core/auth/auth.service';
  33 +import { PersistentRpc } from '@shared/models/rpc.models';
33 34
34 35 @Injectable({
35 36 providedIn: 'root'
... ... @@ -137,6 +138,14 @@ export class DeviceService {
137 138 return this.http.post<Device>(`/api/rpc/twoway/${deviceId}`, requestBody, defaultHttpOptionsFromConfig(config));
138 139 }
139 140
  141 + public getPersistedRpc(rpcId: string, fullResponse = false,
  142 + config?: RequestConfig): Observable<PersistentRpc | HttpResponse<PersistentRpc>> {
  143 + return this.http.get<PersistentRpc>(`/api/rpc/persistent/${rpcId}`, {
  144 + ...defaultHttpOptionsFromConfig(config),
  145 + observe: fullResponse ? 'response' : undefined
  146 + });
  147 + }
  148 +
140 149 public findByQuery(query: DeviceSearchQuery,
141 150 config?: RequestConfig): Observable<Array<Device>> {
142 151 return this.http.post<Array<Device>>('/api/devices', query, defaultHttpOptionsFromConfig(config));
... ... @@ -170,7 +179,7 @@ export class DeviceService {
170 179 public getEdgeDevices(edgeId: string, pageLink: PageLink, type: string = '',
171 180 config?: RequestConfig): Observable<PageData<DeviceInfo>> {
172 181 return this.http.get<PageData<DeviceInfo>>(`/api/edge/${edgeId}/devices${pageLink.toQuery()}&type=${type}`,
173   - defaultHttpOptionsFromConfig(config))
  182 + defaultHttpOptionsFromConfig(config));
174 183 }
175 184
176 185 }
... ...
... ... @@ -36,6 +36,8 @@ interface KnobSettings {
36 36 getValueMethod: string;
37 37 setValueMethod: string;
38 38 requestTimeout: number;
  39 + requestPersistent: boolean;
  40 + persistentPollingInterval: number;
39 41 }
40 42
41 43 @Component({
... ... @@ -80,6 +82,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
80 82
81 83 private isSimulated: boolean;
82 84 private requestTimeout: number;
  85 + private requestPersistent: boolean;
  86 + private persistentPollingInterval: number;
83 87 private getValueMethod: string;
84 88 private setValueMethod: string;
85 89
... ... @@ -138,6 +142,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
138 142 if (this.knobResize$) {
139 143 this.knobResize$.disconnect();
140 144 }
  145 + this.ctx.controlApi.completedCommand();
141 146 }
142 147
143 148 private init() {
... ... @@ -160,8 +165,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
160 165 maxValue: this.maxValue,
161 166 gaugeType: 'donut',
162 167 dashThickness: 2,
163   - donutStartAngle: 3/4*Math.PI,
164   - donutEndAngle: 9/4*Math.PI,
  168 + donutStartAngle: 3 / 4 * Math.PI,
  169 + donutEndAngle: 9 / 4 * Math.PI,
165 170 animation: false
166 171 };
167 172
... ... @@ -209,10 +214,10 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
209 214 e.preventDefault();
210 215 const offset = this.knob.offset();
211 216 const center = {
212   - y : offset.top + this.knob.height()/2,
213   - x: offset.left + this.knob.width()/2
  217 + y: offset.top + this.knob.height() / 2,
  218 + x: offset.left + this.knob.width() / 2
214 219 };
215   - const rad2deg = 180/Math.PI;
  220 + const rad2deg = 180 / Math.PI;
216 221
217 222 $(document).on('mousemove.rem touchmove.rem', (ev) => {
218 223 this.moving = true;
... ... @@ -220,21 +225,20 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
220 225
221 226 const a = center.y - t.pageY;
222 227 const b = center.x - t.pageX;
223   - let deg = Math.atan2(a,b)*rad2deg;
224   - if(deg < 0){
  228 + let deg = Math.atan2(a, b) * rad2deg;
  229 + if (deg < 0) {
225 230 deg = 360 + deg;
226 231 }
227 232
228   - if(this.startDeg === -1){
  233 + if (this.startDeg === -1) {
229 234 this.startDeg = deg;
230 235 }
231 236
232   - let tmp = Math.floor((deg-this.startDeg) + this.rotation);
  237 + let tmp = Math.floor((deg - this.startDeg) + this.rotation);
233 238
234   - if(tmp < 0){
  239 + if (tmp < 0) {
235 240 tmp = 360 + tmp;
236   - }
237   - else if(tmp > 359){
  241 + } else if (tmp > 359) {
238 242 tmp = tmp % 360;
239 243 }
240 244
... ... @@ -251,7 +255,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
251 255 }
252 256 }
253 257 }
254   - if(Math.abs(tmp - this.lastDeg) > 180){
  258 + if (Math.abs(tmp - this.lastDeg) > 180) {
255 259 this.startDeg = deg;
256 260 this.rotation = this.currentDeg;
257 261 return false;
... ... @@ -260,12 +264,12 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
260 264 this.currentDeg = tmp;
261 265 this.lastDeg = tmp;
262 266
263   - this.knobTopPointerContainer.css('transform','rotate('+(this.currentDeg)+'deg)');
  267 + this.knobTopPointerContainer.css('transform', 'rotate(' + (this.currentDeg) + 'deg)');
264 268 this.turn(this.degreeToRatio(this.currentDeg));
265 269 });
266 270
267   - $(document).on('mouseup.rem touchend.rem',() => {
268   - if(this.newValue !== this.rpcValue && this.moving) {
  271 + $(document).on('mouseup.rem touchend.rem', () => {
  272 + if (this.newValue !== this.rpcValue && this.moving) {
269 273 this.rpcUpdateValue(this.newValue);
270 274 }
271 275 this.knob.off('.rem');
... ... @@ -285,6 +289,14 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
285 289 if (settings.requestTimeout) {
286 290 this.requestTimeout = settings.requestTimeout;
287 291 }
  292 + this.requestPersistent = false;
  293 + if (settings.requestPersistent) {
  294 + this.requestPersistent = settings.requestPersistent;
  295 + }
  296 + this.persistentPollingInterval = 5000;
  297 + if (settings.persistentPollingInterval) {
  298 + this.persistentPollingInterval = settings.persistentPollingInterval;
  299 + }
288 300 this.getValueMethod = 'getValue';
289 301 if (settings.getValueMethod && settings.getValueMethod.length) {
290 302 this.getValueMethod = settings.getValueMethod;
... ... @@ -312,15 +324,15 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
312 324 }
313 325
314 326 private degreeToRatio(degree: number): number {
315   - return (degree-this.minDeg)/(this.maxDeg-this.minDeg);
  327 + return (degree - this.minDeg) / (this.maxDeg - this.minDeg);
316 328 }
317 329
318 330 private ratioToDegree(ratio: number): number {
319   - return this.minDeg + ratio*(this.maxDeg-this.minDeg);
  331 + return this.minDeg + ratio * (this.maxDeg - this.minDeg);
320 332 }
321 333
322 334 private turn(ratio: number) {
323   - this.newValue = Number((this.minValue + (this.maxValue - this.minValue)*ratio).toFixed(this.ctx.decimals));
  335 + this.newValue = Number((this.minValue + (this.maxValue - this.minValue) * ratio).toFixed(this.ctx.decimals));
324 336 if (this.canvasBar.value !== this.newValue) {
325 337 this.canvasBar.value = this.newValue;
326 338 }
... ... @@ -339,14 +351,14 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
339 351 this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width());
340 352 this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width());
341 353 const minmaxHeight = this.knobMinmaxContainer.height();
342   - this.minmaxLabel.css({fontSize: minmaxHeight+'px', lineHeight: minmaxHeight+'px'});
  354 + this.minmaxLabel.css({fontSize: minmaxHeight + 'px', lineHeight: minmaxHeight + 'px'});
343 355 this.checkValueSize();
344 356 }
345 357
346 358 private checkValueSize() {
347   - const fontSize = this.knobValueContainer.height()/3.3;
  359 + const fontSize = this.knobValueContainer.height() / 3.3;
348 360 const containerWidth = this.knobValueContainer.width();
349   - this.setFontSize(this.knobValue, this.value+'', fontSize, containerWidth);
  361 + this.setFontSize(this.knobValue, this.value + '', fontSize, containerWidth);
350 362 }
351 363
352 364 private setFontSize(element: JQuery<HTMLElement>, text: string, fontSize: number, maxWidth: number) {
... ... @@ -358,19 +370,19 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
358 370 }
359 371 textWidth = this.measureTextWidth(text, fontSize);
360 372 }
361   - element.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
  373 + element.css({fontSize: fontSize + 'px', lineHeight: fontSize + 'px'});
362 374 }
363 375
364 376 private measureTextWidth(text: string, fontSize: number): number {
365   - this.textMeasure.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
  377 + this.textMeasure.css({fontSize: fontSize + 'px', lineHeight: fontSize + 'px'});
366 378 this.textMeasure.html(text);
367 379 return this.textMeasure.width();
368 380 }
369 381
370 382 private setValue(value: number) {
371   - const ratio = (value-this.minValue) / (this.maxValue - this.minValue);
  383 + const ratio = (value - this.minValue) / (this.maxValue - this.minValue);
372 384 this.rotation = this.lastDeg = this.currentDeg = this.ratioToDegree(ratio);
373   - this.knobTopPointerContainer.css('transform','rotate('+(this.currentDeg)+'deg)');
  385 + this.knobTopPointerContainer.css('transform', 'rotate(' + (this.currentDeg) + 'deg)');
374 386 if (this.canvasBar.value !== value) {
375 387 this.canvasBar.value = value;
376 388 }
... ... @@ -402,7 +414,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
402 414
403 415 private rpcRequestValue() {
404 416 this.error = '';
405   - this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout).subscribe(
  417 + this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout,
  418 + this.requestPersistent, this.persistentPollingInterval).subscribe(
406 419 (responseBody) => {
407 420 if (isNumber(responseBody)) {
408 421 const numValue = Number(Number(responseBody).toFixed(this.ctx.decimals));
... ... @@ -429,7 +442,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy {
429 442 this.executingUpdateValue = true;
430 443 }
431 444 this.error = '';
432   - this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, value, this.requestTimeout).subscribe(
  445 + this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, value, this.requestTimeout,
  446 + this.requestPersistent, this.persistentPollingInterval).subscribe(
433 447 () => {
434 448 this.executingUpdateValue = false;
435 449 if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
... ...
... ... @@ -44,6 +44,8 @@ interface LedIndicatorSettings {
44 44 valueAttribute: string;
45 45 parseValueFunction: string;
46 46 requestTimeout: number;
  47 + requestPersistent: boolean;
  48 + persistentPollingInterval: number;
47 49 }
48 50
49 51 @Component({
... ... @@ -77,6 +79,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe
77 79
78 80 private isSimulated: boolean;
79 81 private requestTimeout: number;
  82 + private requestPersistent: boolean;
  83 + private persistentPollingInterval: number;
80 84 private retrieveValueMethod: RetrieveValueMethod;
81 85 private parseValueFunction: (data: any) => boolean;
82 86 private performCheckStatus: boolean;
... ... @@ -141,6 +145,7 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe
141 145 if (this.ledResize$) {
142 146 this.ledResize$.disconnect();
143 147 }
  148 + this.ctx.controlApi.completedCommand();
144 149 }
145 150
146 151 private init() {
... ... @@ -168,6 +173,14 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe
168 173 if (settings.requestTimeout) {
169 174 this.requestTimeout = settings.requestTimeout;
170 175 }
  176 + this.requestPersistent = false;
  177 + if (settings.requestPersistent) {
  178 + this.requestPersistent = settings.requestPersistent;
  179 + }
  180 + this.persistentPollingInterval = 5000;
  181 + if (settings.persistentPollingInterval) {
  182 + this.persistentPollingInterval = settings.persistentPollingInterval;
  183 + }
171 184 this.retrieveValueMethod = 'attribute';
172 185 if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) {
173 186 this.retrieveValueMethod = settings.retrieveValueMethod;
... ... @@ -219,11 +232,11 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe
219 232 fontSize--;
220 233 textWidth = this.measureTextWidth(text, fontSize);
221 234 }
222   - element.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
  235 + element.css({fontSize: fontSize + 'px', lineHeight: fontSize + 'px'});
223 236 }
224 237
225 238 private measureTextWidth(text: string, fontSize: number): number {
226   - this.textMeasure.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
  239 + this.textMeasure.css({fontSize: fontSize + 'px', lineHeight: fontSize + 'px'});
227 240 this.textMeasure.text(text);
228 241 return this.textMeasure.width();
229 242 }
... ... @@ -259,7 +272,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe
259 272 return;
260 273 }
261 274 this.error = '';
262   - this.ctx.controlApi.sendTwoWayCommand(this.checkStatusMethod, null, this.requestTimeout).subscribe(
  275 + this.ctx.controlApi.sendTwoWayCommand(this.checkStatusMethod, null, this.requestTimeout,
  276 + this.requestPersistent, this.persistentPollingInterval).subscribe(
263 277 (responseBody) => {
264 278 const status = !!responseBody;
265 279 if (status) {
... ... @@ -313,7 +327,7 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe
313 327 );
314 328 }
315 329
316   - private onDataUpdated (subscription: IWidgetSubscription, detectChanges: boolean) {
  330 + private onDataUpdated(subscription: IWidgetSubscription, detectChanges: boolean) {
317 331 let value = false;
318 332 const data = subscription.data;
319 333 if (data.length) {
... ...
... ... @@ -38,6 +38,8 @@ interface RoundSwitchSettings {
38 38 parseValueFunction: string;
39 39 convertValueFunction: string;
40 40 requestTimeout: number;
  41 + requestPersistent: boolean;
  42 + persistentPollingInterval: number;
41 43 }
42 44
43 45 @Component({
... ... @@ -67,6 +69,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
67 69
68 70 private isSimulated: boolean;
69 71 private requestTimeout: number;
  72 + private requestPersistent: boolean;
  73 + private persistentPollingInterval: number;
70 74 private retrieveValueMethod: RetrieveValueMethod;
71 75 private valueKey: string;
72 76 private parseValueFunction: (data: any) => boolean;
... ... @@ -125,6 +129,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
125 129 if (this.switchResize$) {
126 130 this.switchResize$.disconnect();
127 131 }
  132 + this.ctx.controlApi.completedCommand();
128 133 }
129 134
130 135 private init() {
... ... @@ -143,6 +148,14 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
143 148 if (settings.requestTimeout) {
144 149 this.requestTimeout = settings.requestTimeout;
145 150 }
  151 + this.requestPersistent = false;
  152 + if (settings.requestPersistent) {
  153 + this.requestPersistent = settings.requestPersistent;
  154 + }
  155 + this.persistentPollingInterval = 5000;
  156 + if (settings.persistentPollingInterval) {
  157 + this.persistentPollingInterval = settings.persistentPollingInterval;
  158 + }
146 159 this.retrieveValueMethod = 'rpc';
147 160 if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) {
148 161 this.retrieveValueMethod = settings.retrieveValueMethod;
... ... @@ -193,7 +206,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
193 206 const width = this.switchContainer.width();
194 207 const height = this.switchContainer.height();
195 208 const size = Math.min(width, height);
196   - const scale = size/260;
  209 + const scale = size / 260;
197 210 this.switchElement.css({
198 211 '-webkit-transform': `scale(${scale})`,
199 212 '-moz-transform': `scale(${scale})`,
... ... @@ -213,11 +226,11 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
213 226 fontSize--;
214 227 textWidth = this.measureTextWidth(text, fontSize);
215 228 }
216   - element.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
  229 + element.css({fontSize: fontSize + 'px', lineHeight: fontSize + 'px'});
217 230 }
218 231
219 232 private measureTextWidth(text: string, fontSize: number): number {
220   - this.textMeasure.css({fontSize: fontSize+'px', lineHeight: fontSize+'px'});
  233 + this.textMeasure.css({fontSize: fontSize + 'px', lineHeight: fontSize + 'px'});
221 234 this.textMeasure.text(text);
222 235 return this.textMeasure.width();
223 236 }
... ... @@ -239,7 +252,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
239 252
240 253 private rpcRequestValue() {
241 254 this.error = '';
242   - this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout).subscribe(
  255 + this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout,
  256 + this.requestPersistent, this.persistentPollingInterval).subscribe(
243 257 (responseBody) => {
244 258 this.setValue(this.parseValueFunction(responseBody));
245 259 },
... ... @@ -260,7 +274,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes
260 274 this.executingUpdateValue = true;
261 275 }
262 276 this.error = '';
263   - this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, this.convertValueFunction(value), this.requestTimeout).subscribe(
  277 + this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, this.convertValueFunction(value), this.requestTimeout,
  278 + this.requestPersistent, this.persistentPollingInterval).subscribe(
264 279 () => {
265 280 this.executingUpdateValue = false;
266 281 if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
... ...
... ... @@ -42,6 +42,8 @@ interface SwitchSettings {
42 42 parseValueFunction: string;
43 43 convertValueFunction: string;
44 44 requestTimeout: number;
  45 + requestPersistent: boolean;
  46 + persistentPollingInterval: number;
45 47 }
46 48
47 49 @Component({
... ... @@ -74,6 +76,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy
74 76
75 77 private isSimulated: boolean;
76 78 private requestTimeout: number;
  79 + private requestPersistent: boolean;
  80 + private persistentPollingInterval: number;
77 81 private retrieveValueMethod: RetrieveValueMethod;
78 82 private valueKey: string;
79 83 private parseValueFunction: (data: any) => boolean;
... ... @@ -133,6 +137,7 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy
133 137 if (this.switchResize$) {
134 138 this.switchResize$.disconnect();
135 139 }
  140 + this.ctx.controlApi.completedCommand();
136 141 }
137 142
138 143 private init() {
... ... @@ -152,6 +157,14 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy
152 157 if (settings.requestTimeout) {
153 158 this.requestTimeout = settings.requestTimeout;
154 159 }
  160 + this.requestPersistent = false;
  161 + if (settings.requestPersistent) {
  162 + this.requestPersistent = settings.requestPersistent;
  163 + }
  164 + this.persistentPollingInterval = 5000;
  165 + if (settings.persistentPollingInterval) {
  166 + this.persistentPollingInterval = settings.persistentPollingInterval;
  167 + }
155 168 this.retrieveValueMethod = 'rpc';
156 169 if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) {
157 170 this.retrieveValueMethod = settings.retrieveValueMethod;
... ... @@ -257,7 +270,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy
257 270
258 271 private rpcRequestValue() {
259 272 this.error = '';
260   - this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout).subscribe(
  273 + this.ctx.controlApi.sendTwoWayCommand(this.getValueMethod, null, this.requestTimeout,
  274 + this.requestPersistent, this.persistentPollingInterval).subscribe(
261 275 (responseBody) => {
262 276 this.setValue(this.parseValueFunction(responseBody));
263 277 this.ctx.detectChanges();
... ... @@ -279,7 +293,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy
279 293 this.executingUpdateValue = true;
280 294 }
281 295 this.error = '';
282   - this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, this.convertValueFunction(value), this.requestTimeout).subscribe(
  296 + this.ctx.controlApi.sendOneWayCommand(this.setValueMethod, this.convertValueFunction(value), this.requestTimeout,
  297 + this.requestPersistent, this.persistentPollingInterval).subscribe(
283 298 () => {
284 299 this.executingUpdateValue = false;
285 300 if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) {
... ...
... ... @@ -202,6 +202,13 @@ export class WidgetContext {
202 202 } else {
203 203 return of(null);
204 204 }
  205 + },
  206 + completedCommand: () => {
  207 + if (this.defaultSubscription) {
  208 + return this.defaultSubscription.completedCommand();
  209 + } else {
  210 + return of(null);
  211 + }
205 212 }
206 213 };
207 214
... ...
... ... @@ -471,6 +471,12 @@ export const widgetContextCompletions: TbEditorCompletions = {
471 471 description: 'RPC request persistent',
472 472 type: 'boolean',
473 473 optional: true
  474 + },
  475 + {
  476 + name: 'persistentPollingInterval',
  477 + description: 'Polling interval in milliseconds to get persistent RPC command response',
  478 + type: 'number',
  479 + optional: true
474 480 }
475 481 ],
476 482 return: {
... ... @@ -504,6 +510,12 @@ export const widgetContextCompletions: TbEditorCompletions = {
504 510 description: 'RPC request persistent',
505 511 type: 'boolean',
506 512 optional: true
  513 + },
  514 + {
  515 + name: 'persistentPollingInterval',
  516 + description: 'Polling interval in milliseconds to get persistent RPC command response',
  517 + type: 'number',
  518 + optional: true
507 519 }
508 520 ],
509 521 return: {
... ...
... ... @@ -35,7 +35,8 @@ export enum EntityType {
35 35 WIDGET_TYPE = 'WIDGET_TYPE',
36 36 API_USAGE_STATE = 'API_USAGE_STATE',
37 37 TB_RESOURCE = 'TB_RESOURCE',
38   - OTA_PACKAGE = 'OTA_PACKAGE'
  38 + OTA_PACKAGE = 'OTA_PACKAGE',
  39 + RPC = 'RPC'
39 40 }
40 41
41 42 export enum AliasEntityType {
... ...
... ... @@ -26,6 +26,8 @@ export * from './entity-id';
26 26 export * from './entity-view-id';
27 27 export * from './event-id';
28 28 export * from './has-uuid';
  29 +export * from './ota-package-id';
  30 +export * from './rpc-id';
29 31 export * from './rule-chain-id';
30 32 export * from './rule-node-id';
31 33 export * from './tenant-id';
... ...
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { EntityId } from '@shared/models/id/entity-id';
  18 +import { EntityType } from '@shared/models/entity-type.models';
  19 +
  20 +export class RpcId implements EntityId {
  21 + entityType = EntityType.RPC;
  22 + id: string;
  23 + constructor(id: string) {
  24 + this.id = id;
  25 + }
  26 +}
... ...
... ... @@ -41,6 +41,7 @@ export * from './oauth2.models';
41 41 export * from './queue.models';
42 42 export * from './relation.models';
43 43 export * from './resource.models';
  44 +export * from './rpc.models';
44 45 export * from './rule-chain.models';
45 46 export * from './rule-node.models';
46 47 export * from './settings.models';
... ...
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { TenantId } from '@shared/models/id/tenant-id';
  18 +import { RpcId } from '@shared/models/id/rpc-id';
  19 +import { DeviceId } from '@shared/models/id/device-id';
  20 +
  21 +export enum RpcStatus {
  22 + QUEUED = 'QUEUED',
  23 + DELIVERED = 'DELIVERED',
  24 + SUCCESSFUL = 'SUCCESSFUL',
  25 + TIMEOUT = 'TIMEOUT',
  26 + FAILED = 'FAILED'
  27 +}
  28 +
  29 +export interface PersistentRpc {
  30 + id: RpcId;
  31 + createdTime: number;
  32 + expirationTime: number;
  33 + status: RpcStatus;
  34 + response: any;
  35 + request: {
  36 + id: string;
  37 + };
  38 + deviceId: DeviceId;
  39 + tenantId: TenantId;
  40 +}
... ...