Commit 90c84f84d0a8e56523fca9bc7bd8a931340fdaaf
Committed by
Andrew Shvayka
1 parent
b0c6ce9f
UI: Add control widget support persistent command (Add settings: persistent and …
…persistentPollingInterval)
Showing
16 changed files
with
266 additions
and
67 deletions
@@ -18,8 +18,8 @@ | @@ -18,8 +18,8 @@ | ||
18 | "resources": [], | 18 | "resources": [], |
19 | "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>", | 19 | "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>", |
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", | 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 | "dataKeySettingsSchema": "{}\n", | 23 | "dataKeySettingsSchema": "{}\n", |
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\":{}}" | 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,7 +55,7 @@ | ||
55 | "templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>", | 55 | "templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>", |
56 | "templateCss": "", | 56 | "templateCss": "", |
57 | "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", | 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 | "dataKeySettingsSchema": "{}\n", | 59 | "dataKeySettingsSchema": "{}\n", |
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}" | 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,7 +73,7 @@ | ||
73 | "templateHtml": "<tb-switch [ctx]='ctx'></tb-switch>", | 73 | "templateHtml": "<tb-switch [ctx]='ctx'></tb-switch>", |
74 | "templateCss": "", | 74 | "templateCss": "", |
75 | "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", | 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 | "dataKeySettingsSchema": "{}\n", | 77 | "dataKeySettingsSchema": "{}\n", |
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}" | 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,7 +91,7 @@ | ||
91 | "templateHtml": "<tb-round-switch [ctx]='ctx'></tb-round-switch>", | 91 | "templateHtml": "<tb-round-switch [ctx]='ctx'></tb-round-switch>", |
92 | "templateCss": "", | 92 | "templateCss": "", |
93 | "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", | 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 | "dataKeySettingsSchema": "{}\n", | 95 | "dataKeySettingsSchema": "{}\n", |
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}" | 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,7 +109,7 @@ | ||
109 | "templateHtml": "<tb-led-indicator [ctx]='ctx'></tb-led-indicator>", | 109 | "templateHtml": "<tb-led-indicator [ctx]='ctx'></tb-led-indicator>", |
110 | "templateCss": "", | 110 | "templateCss": "", |
111 | "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", | 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 | "dataKeySettingsSchema": "{}\n", | 113 | "dataKeySettingsSchema": "{}\n", |
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}" | 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,8 +126,8 @@ | ||
126 | "resources": [], | 126 | "resources": [], |
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>", | 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 | "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}", | 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 | "dataKeySettingsSchema": "{}\n", | 131 | "dataKeySettingsSchema": "{}\n", |
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\":{}}" | 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,11 +69,27 @@ public class RpcV2Controller extends AbstractRpcController { | ||
69 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 69 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
70 | @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET) | 70 | @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET) |
71 | @ResponseBody | 71 | @ResponseBody |
72 | - public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException { | 72 | + public ResponseEntity<Rpc> getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException { |
73 | checkParameter("RpcId", strRpc); | 73 | checkParameter("RpcId", strRpc); |
74 | try { | 74 | try { |
75 | RpcId rpcId = new RpcId(UUID.fromString(strRpc)); | 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 | } catch (Exception e) { | 93 | } catch (Exception e) { |
78 | throw handleException(e); | 94 | throw handleException(e); |
79 | } | 95 | } |
@@ -69,8 +69,11 @@ export interface WidgetSubscriptionApi { | @@ -69,8 +69,11 @@ export interface WidgetSubscriptionApi { | ||
69 | } | 69 | } |
70 | 70 | ||
71 | export interface RpcApi { | 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 | export interface IWidgetUtils { | 79 | export interface IWidgetUtils { |
@@ -301,8 +304,10 @@ export interface IWidgetSubscription { | @@ -301,8 +304,10 @@ export interface IWidgetSubscription { | ||
301 | onResetTimewindow(): void; | 304 | onResetTimewindow(): void; |
302 | updateTimewindowConfig(newTimewindow: Timewindow): void; | 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 | clearRpcError(): void; | 311 | clearRpcError(): void; |
307 | 312 | ||
308 | subscribe(): void; | 313 | subscribe(): void; |
@@ -35,7 +35,7 @@ import { | @@ -35,7 +35,7 @@ import { | ||
35 | LegendKeyData, | 35 | LegendKeyData, |
36 | widgetType | 36 | widgetType |
37 | } from '@app/shared/models/widget.models'; | 37 | } from '@app/shared/models/widget.models'; |
38 | -import { HttpErrorResponse } from '@angular/common/http'; | 38 | +import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; |
39 | import { | 39 | import { |
40 | calculateIntervalStartEndTime, | 40 | calculateIntervalStartEndTime, |
41 | calculateTsOffset, | 41 | calculateTsOffset, |
@@ -49,7 +49,7 @@ import { | @@ -49,7 +49,7 @@ import { | ||
49 | toHistoryTimewindow, | 49 | toHistoryTimewindow, |
50 | WidgetTimewindow | 50 | WidgetTimewindow |
51 | } from '@app/shared/models/time/time.models'; | 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 | import { CancelAnimationFrame } from '@core/services/raf.service'; | 53 | import { CancelAnimationFrame } from '@core/services/raf.service'; |
54 | import { EntityType } from '@shared/models/entity-type.models'; | 54 | import { EntityType } from '@shared/models/entity-type.models'; |
55 | import { createLabelFromDatasource, deepClone, isDefined, isDefinedAndNotNull, isEqual } from '@core/utils'; | 55 | import { createLabelFromDatasource, deepClone, isDefined, isDefinedAndNotNull, isEqual } from '@core/utils'; |
@@ -67,8 +67,9 @@ import { | @@ -67,8 +67,9 @@ import { | ||
67 | KeyFilter, | 67 | KeyFilter, |
68 | updateDatasourceFromEntityInfo | 68 | updateDatasourceFromEntityInfo |
69 | } from '@shared/models/query/query.models'; | 69 | } from '@shared/models/query/query.models'; |
70 | -import { map } from 'rxjs/operators'; | 70 | +import { filter, map, switchMap, takeUntil } from 'rxjs/operators'; |
71 | import { AlarmDataListener } from '@core/api/alarm-data.service'; | 71 | import { AlarmDataListener } from '@core/api/alarm-data.service'; |
72 | +import { PersistentRpc } from '@shared/models/rpc.models'; | ||
72 | 73 | ||
73 | const moment = moment_; | 74 | const moment = moment_; |
74 | 75 | ||
@@ -644,12 +645,14 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -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 | clearRpcError(): void { | 658 | clearRpcError(): void { |
@@ -658,8 +661,15 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -658,8 +661,15 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
658 | this.callbacks.onRpcErrorCleared(this); | 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 | if (!this.rpcEnabled) { | 673 | if (!this.rpcEnabled) { |
664 | return throwError(new Error('Rpc disabled!')); | 674 | return throwError(new Error('Rpc disabled!')); |
665 | } else { | 675 | } else { |
@@ -677,7 +687,7 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -677,7 +687,7 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
677 | if (timeout && timeout > 0) { | 687 | if (timeout && timeout > 0) { |
678 | requestBody.timeout = timeout; | 688 | requestBody.timeout = timeout; |
679 | } | 689 | } |
680 | - const rpcSubject: Subject<any> = new ReplaySubject<any>(); | 690 | + const rpcSubject: Subject<any> = new Subject<any>(); |
681 | this.executingRpcRequest = true; | 691 | this.executingRpcRequest = true; |
682 | this.callbacks.rpcStateChanged(this); | 692 | this.callbacks.rpcStateChanged(this); |
683 | if (this.ctx.utils.widgetEditMode) { | 693 | if (this.ctx.utils.widgetEditMode) { |
@@ -695,7 +705,19 @@ export class WidgetSubscription implements IWidgetSubscription { | @@ -695,7 +705,19 @@ export class WidgetSubscription implements IWidgetSubscription { | ||
695 | } else { | 705 | } else { |
696 | this.executingSubjects.push(rpcSubject); | 706 | this.executingSubjects.push(rpcSubject); |
697 | (oneWayElseTwoWay ? this.ctx.deviceService.sendOneWayRpcCommand(this.targetDeviceId, requestBody) : | 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 | .subscribe((responseBody) => { | 721 | .subscribe((responseBody) => { |
700 | this.rpcRejection = null; | 722 | this.rpcRejection = null; |
701 | this.rpcErrorText = null; | 723 | this.rpcErrorText = null; |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | import { Injectable } from '@angular/core'; | 17 | import { Injectable } from '@angular/core'; |
18 | import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; | 18 | import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; |
19 | import { Observable, ReplaySubject } from 'rxjs'; | 19 | import { Observable, ReplaySubject } from 'rxjs'; |
20 | -import { HttpClient } from '@angular/common/http'; | 20 | +import { HttpClient, HttpResponse } from '@angular/common/http'; |
21 | import { PageLink } from '@shared/models/page/page-link'; | 21 | import { PageLink } from '@shared/models/page/page-link'; |
22 | import { PageData } from '@shared/models/page/page-data'; | 22 | import { PageData } from '@shared/models/page/page-data'; |
23 | import { | 23 | import { |
@@ -30,6 +30,7 @@ import { | @@ -30,6 +30,7 @@ import { | ||
30 | } from '@app/shared/models/device.models'; | 30 | } from '@app/shared/models/device.models'; |
31 | import { EntitySubtype } from '@app/shared/models/entity-type.models'; | 31 | import { EntitySubtype } from '@app/shared/models/entity-type.models'; |
32 | import { AuthService } from '@core/auth/auth.service'; | 32 | import { AuthService } from '@core/auth/auth.service'; |
33 | +import { PersistentRpc } from '@shared/models/rpc.models'; | ||
33 | 34 | ||
34 | @Injectable({ | 35 | @Injectable({ |
35 | providedIn: 'root' | 36 | providedIn: 'root' |
@@ -137,6 +138,14 @@ export class DeviceService { | @@ -137,6 +138,14 @@ export class DeviceService { | ||
137 | return this.http.post<Device>(`/api/rpc/twoway/${deviceId}`, requestBody, defaultHttpOptionsFromConfig(config)); | 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 | public findByQuery(query: DeviceSearchQuery, | 149 | public findByQuery(query: DeviceSearchQuery, |
141 | config?: RequestConfig): Observable<Array<Device>> { | 150 | config?: RequestConfig): Observable<Array<Device>> { |
142 | return this.http.post<Array<Device>>('/api/devices', query, defaultHttpOptionsFromConfig(config)); | 151 | return this.http.post<Array<Device>>('/api/devices', query, defaultHttpOptionsFromConfig(config)); |
@@ -170,7 +179,7 @@ export class DeviceService { | @@ -170,7 +179,7 @@ export class DeviceService { | ||
170 | public getEdgeDevices(edgeId: string, pageLink: PageLink, type: string = '', | 179 | public getEdgeDevices(edgeId: string, pageLink: PageLink, type: string = '', |
171 | config?: RequestConfig): Observable<PageData<DeviceInfo>> { | 180 | config?: RequestConfig): Observable<PageData<DeviceInfo>> { |
172 | return this.http.get<PageData<DeviceInfo>>(`/api/edge/${edgeId}/devices${pageLink.toQuery()}&type=${type}`, | 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,6 +36,8 @@ interface KnobSettings { | ||
36 | getValueMethod: string; | 36 | getValueMethod: string; |
37 | setValueMethod: string; | 37 | setValueMethod: string; |
38 | requestTimeout: number; | 38 | requestTimeout: number; |
39 | + requestPersistent: boolean; | ||
40 | + persistentPollingInterval: number; | ||
39 | } | 41 | } |
40 | 42 | ||
41 | @Component({ | 43 | @Component({ |
@@ -80,6 +82,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -80,6 +82,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
80 | 82 | ||
81 | private isSimulated: boolean; | 83 | private isSimulated: boolean; |
82 | private requestTimeout: number; | 84 | private requestTimeout: number; |
85 | + private requestPersistent: boolean; | ||
86 | + private persistentPollingInterval: number; | ||
83 | private getValueMethod: string; | 87 | private getValueMethod: string; |
84 | private setValueMethod: string; | 88 | private setValueMethod: string; |
85 | 89 | ||
@@ -138,6 +142,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -138,6 +142,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
138 | if (this.knobResize$) { | 142 | if (this.knobResize$) { |
139 | this.knobResize$.disconnect(); | 143 | this.knobResize$.disconnect(); |
140 | } | 144 | } |
145 | + this.ctx.controlApi.completedCommand(); | ||
141 | } | 146 | } |
142 | 147 | ||
143 | private init() { | 148 | private init() { |
@@ -160,8 +165,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -160,8 +165,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
160 | maxValue: this.maxValue, | 165 | maxValue: this.maxValue, |
161 | gaugeType: 'donut', | 166 | gaugeType: 'donut', |
162 | dashThickness: 2, | 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 | animation: false | 170 | animation: false |
166 | }; | 171 | }; |
167 | 172 | ||
@@ -209,10 +214,10 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -209,10 +214,10 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
209 | e.preventDefault(); | 214 | e.preventDefault(); |
210 | const offset = this.knob.offset(); | 215 | const offset = this.knob.offset(); |
211 | const center = { | 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 | $(document).on('mousemove.rem touchmove.rem', (ev) => { | 222 | $(document).on('mousemove.rem touchmove.rem', (ev) => { |
218 | this.moving = true; | 223 | this.moving = true; |
@@ -220,21 +225,20 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -220,21 +225,20 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
220 | 225 | ||
221 | const a = center.y - t.pageY; | 226 | const a = center.y - t.pageY; |
222 | const b = center.x - t.pageX; | 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 | deg = 360 + deg; | 230 | deg = 360 + deg; |
226 | } | 231 | } |
227 | 232 | ||
228 | - if(this.startDeg === -1){ | 233 | + if (this.startDeg === -1) { |
229 | this.startDeg = deg; | 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 | tmp = 360 + tmp; | 240 | tmp = 360 + tmp; |
236 | - } | ||
237 | - else if(tmp > 359){ | 241 | + } else if (tmp > 359) { |
238 | tmp = tmp % 360; | 242 | tmp = tmp % 360; |
239 | } | 243 | } |
240 | 244 | ||
@@ -251,7 +255,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -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 | this.startDeg = deg; | 259 | this.startDeg = deg; |
256 | this.rotation = this.currentDeg; | 260 | this.rotation = this.currentDeg; |
257 | return false; | 261 | return false; |
@@ -260,12 +264,12 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -260,12 +264,12 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
260 | this.currentDeg = tmp; | 264 | this.currentDeg = tmp; |
261 | this.lastDeg = tmp; | 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 | this.turn(this.degreeToRatio(this.currentDeg)); | 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 | this.rpcUpdateValue(this.newValue); | 273 | this.rpcUpdateValue(this.newValue); |
270 | } | 274 | } |
271 | this.knob.off('.rem'); | 275 | this.knob.off('.rem'); |
@@ -285,6 +289,14 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -285,6 +289,14 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
285 | if (settings.requestTimeout) { | 289 | if (settings.requestTimeout) { |
286 | this.requestTimeout = settings.requestTimeout; | 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 | this.getValueMethod = 'getValue'; | 300 | this.getValueMethod = 'getValue'; |
289 | if (settings.getValueMethod && settings.getValueMethod.length) { | 301 | if (settings.getValueMethod && settings.getValueMethod.length) { |
290 | this.getValueMethod = settings.getValueMethod; | 302 | this.getValueMethod = settings.getValueMethod; |
@@ -312,15 +324,15 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -312,15 +324,15 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
312 | } | 324 | } |
313 | 325 | ||
314 | private degreeToRatio(degree: number): number { | 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 | private ratioToDegree(ratio: number): number { | 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 | private turn(ratio: number) { | 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 | if (this.canvasBar.value !== this.newValue) { | 336 | if (this.canvasBar.value !== this.newValue) { |
325 | this.canvasBar.value = this.newValue; | 337 | this.canvasBar.value = this.newValue; |
326 | } | 338 | } |
@@ -339,14 +351,14 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -339,14 +351,14 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
339 | this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width()); | 351 | this.setFontSize(this.knobTitle, this.title, this.knobTitleContainer.height(), this.knobTitleContainer.width()); |
340 | this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width()); | 352 | this.setFontSize(this.knobError, this.error, this.knobErrorContainer.height(), this.knobErrorContainer.width()); |
341 | const minmaxHeight = this.knobMinmaxContainer.height(); | 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 | this.checkValueSize(); | 355 | this.checkValueSize(); |
344 | } | 356 | } |
345 | 357 | ||
346 | private checkValueSize() { | 358 | private checkValueSize() { |
347 | - const fontSize = this.knobValueContainer.height()/3.3; | 359 | + const fontSize = this.knobValueContainer.height() / 3.3; |
348 | const containerWidth = this.knobValueContainer.width(); | 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 | private setFontSize(element: JQuery<HTMLElement>, text: string, fontSize: number, maxWidth: number) { | 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,19 +370,19 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
358 | } | 370 | } |
359 | textWidth = this.measureTextWidth(text, fontSize); | 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 | private measureTextWidth(text: string, fontSize: number): number { | 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 | this.textMeasure.html(text); | 378 | this.textMeasure.html(text); |
367 | return this.textMeasure.width(); | 379 | return this.textMeasure.width(); |
368 | } | 380 | } |
369 | 381 | ||
370 | private setValue(value: number) { | 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 | this.rotation = this.lastDeg = this.currentDeg = this.ratioToDegree(ratio); | 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 | if (this.canvasBar.value !== value) { | 386 | if (this.canvasBar.value !== value) { |
375 | this.canvasBar.value = value; | 387 | this.canvasBar.value = value; |
376 | } | 388 | } |
@@ -402,7 +414,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -402,7 +414,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
402 | 414 | ||
403 | private rpcRequestValue() { | 415 | private rpcRequestValue() { |
404 | this.error = ''; | 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 | (responseBody) => { | 419 | (responseBody) => { |
407 | if (isNumber(responseBody)) { | 420 | if (isNumber(responseBody)) { |
408 | const numValue = Number(Number(responseBody).toFixed(this.ctx.decimals)); | 421 | const numValue = Number(Number(responseBody).toFixed(this.ctx.decimals)); |
@@ -429,7 +442,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | @@ -429,7 +442,8 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { | ||
429 | this.executingUpdateValue = true; | 442 | this.executingUpdateValue = true; |
430 | } | 443 | } |
431 | this.error = ''; | 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 | this.executingUpdateValue = false; | 448 | this.executingUpdateValue = false; |
435 | if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { | 449 | if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { |
@@ -44,6 +44,8 @@ interface LedIndicatorSettings { | @@ -44,6 +44,8 @@ interface LedIndicatorSettings { | ||
44 | valueAttribute: string; | 44 | valueAttribute: string; |
45 | parseValueFunction: string; | 45 | parseValueFunction: string; |
46 | requestTimeout: number; | 46 | requestTimeout: number; |
47 | + requestPersistent: boolean; | ||
48 | + persistentPollingInterval: number; | ||
47 | } | 49 | } |
48 | 50 | ||
49 | @Component({ | 51 | @Component({ |
@@ -77,6 +79,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | @@ -77,6 +79,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | ||
77 | 79 | ||
78 | private isSimulated: boolean; | 80 | private isSimulated: boolean; |
79 | private requestTimeout: number; | 81 | private requestTimeout: number; |
82 | + private requestPersistent: boolean; | ||
83 | + private persistentPollingInterval: number; | ||
80 | private retrieveValueMethod: RetrieveValueMethod; | 84 | private retrieveValueMethod: RetrieveValueMethod; |
81 | private parseValueFunction: (data: any) => boolean; | 85 | private parseValueFunction: (data: any) => boolean; |
82 | private performCheckStatus: boolean; | 86 | private performCheckStatus: boolean; |
@@ -141,6 +145,7 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | @@ -141,6 +145,7 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | ||
141 | if (this.ledResize$) { | 145 | if (this.ledResize$) { |
142 | this.ledResize$.disconnect(); | 146 | this.ledResize$.disconnect(); |
143 | } | 147 | } |
148 | + this.ctx.controlApi.completedCommand(); | ||
144 | } | 149 | } |
145 | 150 | ||
146 | private init() { | 151 | private init() { |
@@ -168,6 +173,14 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | @@ -168,6 +173,14 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | ||
168 | if (settings.requestTimeout) { | 173 | if (settings.requestTimeout) { |
169 | this.requestTimeout = settings.requestTimeout; | 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 | this.retrieveValueMethod = 'attribute'; | 184 | this.retrieveValueMethod = 'attribute'; |
172 | if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) { | 185 | if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) { |
173 | this.retrieveValueMethod = settings.retrieveValueMethod; | 186 | this.retrieveValueMethod = settings.retrieveValueMethod; |
@@ -219,11 +232,11 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | @@ -219,11 +232,11 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | ||
219 | fontSize--; | 232 | fontSize--; |
220 | textWidth = this.measureTextWidth(text, fontSize); | 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 | private measureTextWidth(text: string, fontSize: number): number { | 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 | this.textMeasure.text(text); | 240 | this.textMeasure.text(text); |
228 | return this.textMeasure.width(); | 241 | return this.textMeasure.width(); |
229 | } | 242 | } |
@@ -259,7 +272,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | @@ -259,7 +272,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | ||
259 | return; | 272 | return; |
260 | } | 273 | } |
261 | this.error = ''; | 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 | (responseBody) => { | 277 | (responseBody) => { |
264 | const status = !!responseBody; | 278 | const status = !!responseBody; |
265 | if (status) { | 279 | if (status) { |
@@ -313,7 +327,7 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe | @@ -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 | let value = false; | 331 | let value = false; |
318 | const data = subscription.data; | 332 | const data = subscription.data; |
319 | if (data.length) { | 333 | if (data.length) { |
@@ -38,6 +38,8 @@ interface RoundSwitchSettings { | @@ -38,6 +38,8 @@ interface RoundSwitchSettings { | ||
38 | parseValueFunction: string; | 38 | parseValueFunction: string; |
39 | convertValueFunction: string; | 39 | convertValueFunction: string; |
40 | requestTimeout: number; | 40 | requestTimeout: number; |
41 | + requestPersistent: boolean; | ||
42 | + persistentPollingInterval: number; | ||
41 | } | 43 | } |
42 | 44 | ||
43 | @Component({ | 45 | @Component({ |
@@ -67,6 +69,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -67,6 +69,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
67 | 69 | ||
68 | private isSimulated: boolean; | 70 | private isSimulated: boolean; |
69 | private requestTimeout: number; | 71 | private requestTimeout: number; |
72 | + private requestPersistent: boolean; | ||
73 | + private persistentPollingInterval: number; | ||
70 | private retrieveValueMethod: RetrieveValueMethod; | 74 | private retrieveValueMethod: RetrieveValueMethod; |
71 | private valueKey: string; | 75 | private valueKey: string; |
72 | private parseValueFunction: (data: any) => boolean; | 76 | private parseValueFunction: (data: any) => boolean; |
@@ -125,6 +129,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -125,6 +129,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
125 | if (this.switchResize$) { | 129 | if (this.switchResize$) { |
126 | this.switchResize$.disconnect(); | 130 | this.switchResize$.disconnect(); |
127 | } | 131 | } |
132 | + this.ctx.controlApi.completedCommand(); | ||
128 | } | 133 | } |
129 | 134 | ||
130 | private init() { | 135 | private init() { |
@@ -143,6 +148,14 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -143,6 +148,14 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
143 | if (settings.requestTimeout) { | 148 | if (settings.requestTimeout) { |
144 | this.requestTimeout = settings.requestTimeout; | 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 | this.retrieveValueMethod = 'rpc'; | 159 | this.retrieveValueMethod = 'rpc'; |
147 | if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) { | 160 | if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) { |
148 | this.retrieveValueMethod = settings.retrieveValueMethod; | 161 | this.retrieveValueMethod = settings.retrieveValueMethod; |
@@ -193,7 +206,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -193,7 +206,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
193 | const width = this.switchContainer.width(); | 206 | const width = this.switchContainer.width(); |
194 | const height = this.switchContainer.height(); | 207 | const height = this.switchContainer.height(); |
195 | const size = Math.min(width, height); | 208 | const size = Math.min(width, height); |
196 | - const scale = size/260; | 209 | + const scale = size / 260; |
197 | this.switchElement.css({ | 210 | this.switchElement.css({ |
198 | '-webkit-transform': `scale(${scale})`, | 211 | '-webkit-transform': `scale(${scale})`, |
199 | '-moz-transform': `scale(${scale})`, | 212 | '-moz-transform': `scale(${scale})`, |
@@ -213,11 +226,11 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -213,11 +226,11 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
213 | fontSize--; | 226 | fontSize--; |
214 | textWidth = this.measureTextWidth(text, fontSize); | 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 | private measureTextWidth(text: string, fontSize: number): number { | 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 | this.textMeasure.text(text); | 234 | this.textMeasure.text(text); |
222 | return this.textMeasure.width(); | 235 | return this.textMeasure.width(); |
223 | } | 236 | } |
@@ -239,7 +252,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -239,7 +252,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
239 | 252 | ||
240 | private rpcRequestValue() { | 253 | private rpcRequestValue() { |
241 | this.error = ''; | 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 | (responseBody) => { | 257 | (responseBody) => { |
244 | this.setValue(this.parseValueFunction(responseBody)); | 258 | this.setValue(this.parseValueFunction(responseBody)); |
245 | }, | 259 | }, |
@@ -260,7 +274,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | @@ -260,7 +274,8 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes | ||
260 | this.executingUpdateValue = true; | 274 | this.executingUpdateValue = true; |
261 | } | 275 | } |
262 | this.error = ''; | 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 | this.executingUpdateValue = false; | 280 | this.executingUpdateValue = false; |
266 | if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { | 281 | if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { |
@@ -42,6 +42,8 @@ interface SwitchSettings { | @@ -42,6 +42,8 @@ interface SwitchSettings { | ||
42 | parseValueFunction: string; | 42 | parseValueFunction: string; |
43 | convertValueFunction: string; | 43 | convertValueFunction: string; |
44 | requestTimeout: number; | 44 | requestTimeout: number; |
45 | + requestPersistent: boolean; | ||
46 | + persistentPollingInterval: number; | ||
45 | } | 47 | } |
46 | 48 | ||
47 | @Component({ | 49 | @Component({ |
@@ -74,6 +76,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | @@ -74,6 +76,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | ||
74 | 76 | ||
75 | private isSimulated: boolean; | 77 | private isSimulated: boolean; |
76 | private requestTimeout: number; | 78 | private requestTimeout: number; |
79 | + private requestPersistent: boolean; | ||
80 | + private persistentPollingInterval: number; | ||
77 | private retrieveValueMethod: RetrieveValueMethod; | 81 | private retrieveValueMethod: RetrieveValueMethod; |
78 | private valueKey: string; | 82 | private valueKey: string; |
79 | private parseValueFunction: (data: any) => boolean; | 83 | private parseValueFunction: (data: any) => boolean; |
@@ -133,6 +137,7 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | @@ -133,6 +137,7 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | ||
133 | if (this.switchResize$) { | 137 | if (this.switchResize$) { |
134 | this.switchResize$.disconnect(); | 138 | this.switchResize$.disconnect(); |
135 | } | 139 | } |
140 | + this.ctx.controlApi.completedCommand(); | ||
136 | } | 141 | } |
137 | 142 | ||
138 | private init() { | 143 | private init() { |
@@ -152,6 +157,14 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | @@ -152,6 +157,14 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | ||
152 | if (settings.requestTimeout) { | 157 | if (settings.requestTimeout) { |
153 | this.requestTimeout = settings.requestTimeout; | 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 | this.retrieveValueMethod = 'rpc'; | 168 | this.retrieveValueMethod = 'rpc'; |
156 | if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) { | 169 | if (settings.retrieveValueMethod && settings.retrieveValueMethod.length) { |
157 | this.retrieveValueMethod = settings.retrieveValueMethod; | 170 | this.retrieveValueMethod = settings.retrieveValueMethod; |
@@ -257,7 +270,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | @@ -257,7 +270,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | ||
257 | 270 | ||
258 | private rpcRequestValue() { | 271 | private rpcRequestValue() { |
259 | this.error = ''; | 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 | (responseBody) => { | 275 | (responseBody) => { |
262 | this.setValue(this.parseValueFunction(responseBody)); | 276 | this.setValue(this.parseValueFunction(responseBody)); |
263 | this.ctx.detectChanges(); | 277 | this.ctx.detectChanges(); |
@@ -279,7 +293,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | @@ -279,7 +293,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy | ||
279 | this.executingUpdateValue = true; | 293 | this.executingUpdateValue = true; |
280 | } | 294 | } |
281 | this.error = ''; | 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 | this.executingUpdateValue = false; | 299 | this.executingUpdateValue = false; |
285 | if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { | 300 | if (this.scheduledValue != null && this.scheduledValue !== this.rpcValue) { |
@@ -202,6 +202,13 @@ export class WidgetContext { | @@ -202,6 +202,13 @@ export class WidgetContext { | ||
202 | } else { | 202 | } else { |
203 | return of(null); | 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,6 +471,12 @@ export const widgetContextCompletions: TbEditorCompletions = { | ||
471 | description: 'RPC request persistent', | 471 | description: 'RPC request persistent', |
472 | type: 'boolean', | 472 | type: 'boolean', |
473 | optional: true | 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 | return: { | 482 | return: { |
@@ -504,6 +510,12 @@ export const widgetContextCompletions: TbEditorCompletions = { | @@ -504,6 +510,12 @@ export const widgetContextCompletions: TbEditorCompletions = { | ||
504 | description: 'RPC request persistent', | 510 | description: 'RPC request persistent', |
505 | type: 'boolean', | 511 | type: 'boolean', |
506 | optional: true | 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 | return: { | 521 | return: { |
@@ -35,7 +35,8 @@ export enum EntityType { | @@ -35,7 +35,8 @@ export enum EntityType { | ||
35 | WIDGET_TYPE = 'WIDGET_TYPE', | 35 | WIDGET_TYPE = 'WIDGET_TYPE', |
36 | API_USAGE_STATE = 'API_USAGE_STATE', | 36 | API_USAGE_STATE = 'API_USAGE_STATE', |
37 | TB_RESOURCE = 'TB_RESOURCE', | 37 | TB_RESOURCE = 'TB_RESOURCE', |
38 | - OTA_PACKAGE = 'OTA_PACKAGE' | 38 | + OTA_PACKAGE = 'OTA_PACKAGE', |
39 | + RPC = 'RPC' | ||
39 | } | 40 | } |
40 | 41 | ||
41 | export enum AliasEntityType { | 42 | export enum AliasEntityType { |
@@ -26,6 +26,8 @@ export * from './entity-id'; | @@ -26,6 +26,8 @@ export * from './entity-id'; | ||
26 | export * from './entity-view-id'; | 26 | export * from './entity-view-id'; |
27 | export * from './event-id'; | 27 | export * from './event-id'; |
28 | export * from './has-uuid'; | 28 | export * from './has-uuid'; |
29 | +export * from './ota-package-id'; | ||
30 | +export * from './rpc-id'; | ||
29 | export * from './rule-chain-id'; | 31 | export * from './rule-chain-id'; |
30 | export * from './rule-node-id'; | 32 | export * from './rule-node-id'; |
31 | export * from './tenant-id'; | 33 | export * from './tenant-id'; |
ui-ngx/src/app/shared/models/id/rpc-id.ts
0 → 100644
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,6 +41,7 @@ export * from './oauth2.models'; | ||
41 | export * from './queue.models'; | 41 | export * from './queue.models'; |
42 | export * from './relation.models'; | 42 | export * from './relation.models'; |
43 | export * from './resource.models'; | 43 | export * from './resource.models'; |
44 | +export * from './rpc.models'; | ||
44 | export * from './rule-chain.models'; | 45 | export * from './rule-chain.models'; |
45 | export * from './rule-node.models'; | 46 | export * from './rule-node.models'; |
46 | export * from './settings.models'; | 47 | export * from './settings.models'; |
ui-ngx/src/app/shared/models/rpc.models.ts
0 → 100644
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 | +} |