diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
index 8833ca67b9..c9e1cd1e6d 100644
--- a/application/src/main/data/json/system/widget_bundles/cards.json
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -26,12 +26,12 @@
"name": "Entities table",
"descriptor": {
"type": "latest",
- "sizeX": 10.5,
- "sizeY": 6.5,
+ "sizeX": 7.5,
+ "sizeY": 4.5,
"resources": [],
"templateHtml": "\n",
"templateCss": "",
- "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('entities-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1 \n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
+ "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('entities-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"entitiesTitle\": {\n \"title\": \"Entities table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSearch\": {\n \"title\": \"Enable entities search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayEntityName\": {\n \"title\": \"Display entity name column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"entityNameColumnTitle\": {\n \"title\": \"Entity name column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityType\": {\n \"title\": \"Display entity type column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"entityName\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"entitiesTitle\",\n \"enableSearch\",\n \"displayEntityName\",\n \"entityNameColumnTitle\",\n \"displayEntityType\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, entity, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"18px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}]}"
diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json
index 9ae8677d5f..24014fc4ba 100644
--- a/application/src/main/data/json/system/widget_bundles/control_widgets.json
+++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json
@@ -15,7 +15,7 @@
"resources": [],
"templateHtml": "
",
"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",
- "controllerScript": "var requestTimeout = 500;\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 var greetings = 'Welcome to ThingsBoard RPC commands 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 = angular.copy(command).trim();\n if (localCommand == 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (cmdObj.args.length && cmdObj.args[0]) {\n params = JSON.parse(cmdObj.args[0]);\n }\n performRpc(this, cmdObj.name, params);\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 });\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 += ' [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\nfunction performRpc(terminal, method, params) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout).then(\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 \nself.onDestroy = function() {\n}\n",
+ "controllerScript": "var requestTimeout = 500;\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 var greetings = 'Welcome to ThingsBoard RPC commands 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 = angular.copy(command).trim();\n if (localCommand == 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (cmdObj.args.length && cmdObj.args[0]) {\n try {\n params = JSON.parse(cmdObj.args[0]);\n } catch (e) {\n params = cmdObj.args[0];\n }\n }\n performRpc(this, cmdObj.name, params);\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 });\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 += ' [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\nfunction performRpc(terminal, method, params) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout).then(\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 \nself.onDestroy = function() {\n}\n",
"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 },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"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\":\"Device terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
diff --git a/ui/src/app/api/alias-controller.js b/ui/src/app/api/alias-controller.js
index 9acd5aaa8b..fd970c0529 100644
--- a/ui/src/app/api/alias-controller.js
+++ b/ui/src/app/api/alias-controller.js
@@ -53,10 +53,11 @@ export default class AliasController {
}
dashboardStateChanged() {
- var newEntityId = this.stateController.getStateParams().entityId;
var changedAliasIds = [];
for (var aliasId in this.resolvedAliasesToStateEntities) {
- var prevEntityId = this.resolvedAliasesToStateEntities[aliasId];
+ var stateEntityInfo = this.resolvedAliasesToStateEntities[aliasId];
+ var newEntityId = this.stateController.getEntityId(stateEntityInfo.entityParamName);
+ var prevEntityId = stateEntityInfo.entityId;
if (!angular.equals(newEntityId, prevEntityId)) {
changedAliasIds.push(aliasId);
this.setAliasUnresolved(aliasId);
@@ -93,19 +94,26 @@ export default class AliasController {
this.entityService.resolveAlias(entityAlias, this.stateController.getStateParams()).then(
function success(aliasInfo) {
aliasCtrl.resolvedAliases[aliasId] = aliasInfo;
+ delete aliasCtrl.resolvedAliasesPromise[aliasId];
if (aliasInfo.stateEntity) {
+ var stateEntityInfo = {
+ entityParamName: aliasInfo.entityParamName,
+ entityId: aliasCtrl.stateController.getEntityId(aliasInfo.entityParamName)
+ };
aliasCtrl.resolvedAliasesToStateEntities[aliasId] =
- aliasCtrl.stateController.getStateParams().entityId;
+ stateEntityInfo;
}
aliasCtrl.$scope.$broadcast('entityAliasResolved', aliasId);
deferred.resolve(aliasInfo);
},
function fail() {
deferred.reject();
+ delete aliasCtrl.resolvedAliasesPromise[aliasId];
}
);
} else {
deferred.reject();
+ delete aliasCtrl.resolvedAliasesPromise[aliasId];
}
return this.resolvedAliasesPromise[aliasId];
}
diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js
index dde622871a..b20d0b375d 100644
--- a/ui/src/app/api/entity.service.js
+++ b/ui/src/app/api/entity.service.js
@@ -376,6 +376,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
var aliasInfo = {
alias: entityAlias.alias,
stateEntity: result.stateEntity,
+ entityParamName: result.entityParamName,
resolveMultiple: filter.resolveMultiple
};
aliasInfo.resolvedEntities = result.entities;
@@ -392,12 +393,30 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
return deferred.promise;
}
+ function getStateEntityId(filter, stateParams) {
+ var entityId = null;
+ if (stateParams) {
+ if (filter.stateEntityParamName && filter.stateEntityParamName.length) {
+ if (stateParams[filter.stateEntityParamName]) {
+ entityId = stateParams[filter.stateEntityParamName].entityId;
+ }
+ } else {
+ entityId = stateParams.entityId;
+ }
+ }
+ return entityId;
+ }
+
function resolveAliasFilter(filter, stateParams, maxItems) {
var deferred = $q.defer();
var result = {
entities: [],
stateEntity: false
};
+ if (filter.stateEntityParamName && filter.stateEntityParamName.length) {
+ result.entityParamName = filter.stateEntityParamName;
+ }
+ var stateEntityId = getStateEntityId(filter, stateParams);
switch (filter.type) {
case types.aliasFilterType.entityList.value:
getEntities(filter.entityType, filter.entityList).then(
@@ -431,8 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
break;
case types.aliasFilterType.stateEntity.value:
result.stateEntity = true;
- if (stateParams && stateParams.entityId) {
- getEntity(stateParams.entityId.entityType, stateParams.entityId.id).then(
+ if (stateEntityId) {
+ getEntity(stateEntityId.entityType, stateEntityId.id).then(
function success(entity) {
result.entities = entitiesToEntitiesInfo([entity]);
deferred.resolve(result);
@@ -479,9 +498,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
result.stateEntity = filter.rootStateEntity;
var rootEntityType;
var rootEntityId;
- if (result.stateEntity && stateParams && stateParams.entityId) {
- rootEntityType = stateParams.entityId.entityType;
- rootEntityId = stateParams.entityId.id;
+ if (result.stateEntity && stateEntityId) {
+ rootEntityType = stateEntityId.entityType;
+ rootEntityId = stateEntityId.id;
} else if (!result.stateEntity) {
rootEntityType = filter.rootEntity.entityType;
rootEntityId = filter.rootEntity.id;
@@ -520,9 +539,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
case types.aliasFilterType.assetSearchQuery.value:
case types.aliasFilterType.deviceSearchQuery.value:
result.stateEntity = filter.rootStateEntity;
- if (result.stateEntity && stateParams && stateParams.entityId) {
- rootEntityType = stateParams.entityId.entityType;
- rootEntityId = stateParams.entityId.id;
+ if (result.stateEntity && stateEntityId) {
+ rootEntityType = stateEntityId.entityType;
+ rootEntityId = stateEntityId.id;
} else if (!result.stateEntity) {
rootEntityType = filter.rootEntity.entityType;
rootEntityId = filter.rootEntity.id;
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index dd93626ed9..8678b9b429 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -730,12 +730,15 @@ export default class Subscription {
index += datasource.dataKeys.length;
this.datasourceListeners.push(listener);
- this.ctx.datasourceService.subscribeToDatasource(listener);
- if (datasource.unresolvedStateEntity) {
+
+ if (datasource.dataKeys.length) {
+ this.ctx.datasourceService.subscribeToDatasource(listener);
+ }
+
+ if (datasource.unresolvedStateEntity || !datasource.dataKeys.length) {
this.notifyDataLoaded();
this.onDataUpdated();
}
-
}
}
}
diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js
index a3019259f2..8580b800bd 100644
--- a/ui/src/app/api/widget.service.js
+++ b/ui/src/app/api/widget.service.js
@@ -556,8 +556,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
' self.typeParameters = function() {\n\n' +
return {
useCustomDatasources: false,
- maxDatasources: -1 //unlimited
- maxDataKeys: -1 //unlimited
+ maxDatasources: -1, //unlimited
+ maxDataKeys: -1, //unlimited
+ dataKeysOptional: false
};
' }\n\n' +
@@ -625,6 +626,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
if (angular.isUndefined(result.typeParameters.maxDataKeys)) {
result.typeParameters.maxDataKeys = -1;
}
+ if (angular.isUndefined(result.typeParameters.dataKeysOptional)) {
+ result.typeParameters.dataKeysOptional = false;
+ }
if (angular.isFunction(widgetTypeInstance.actionSources)) {
result.actionSources = widgetTypeInstance.actionSources();
} else {
diff --git a/ui/src/app/components/datasource-entity.directive.js b/ui/src/app/components/datasource-entity.directive.js
index 46c8b6344f..2cc5d2c09d 100644
--- a/ui/src/app/components/datasource-entity.directive.js
+++ b/ui/src/app/components/datasource-entity.directive.js
@@ -68,10 +68,14 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
ngModelCtrl.$setValidity('entityAlias',
angular.isDefined(value.entityAliasId) &&
value.entityAliasId != null);
- ngModelCtrl.$setValidity('entityKeys',
- angular.isDefined(value.dataKeys) &&
- value.dataKeys != null &&
- value.dataKeys.length > 0);
+ if (scope.optDataKeys) {
+ ngModelCtrl.$setValidity('entityKeys', true);
+ } else {
+ ngModelCtrl.$setValidity('entityKeys',
+ angular.isDefined(value.dataKeys) &&
+ value.dataKeys != null &&
+ value.dataKeys.length > 0);
+ }
}
}
};
@@ -281,6 +285,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc
scope: {
widgetType: '=',
maxDataKeys: '=',
+ optDataKeys: '=',
aliasController: '=',
datakeySettingsSchema: '=',
generateDataKey: '&',
diff --git a/ui/src/app/components/datasource-func.directive.js b/ui/src/app/components/datasource-func.directive.js
index 96e03871c1..5ab1dcd987 100644
--- a/ui/src/app/components/datasource-func.directive.js
+++ b/ui/src/app/components/datasource-func.directive.js
@@ -63,10 +63,14 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
var dataValid = angular.isDefined(value) && value != null;
ngModelCtrl.$setValidity('deviceData', dataValid);
if (dataValid) {
- ngModelCtrl.$setValidity('datasourceKeys',
- angular.isDefined(value.dataKeys) &&
- value.dataKeys != null &&
- value.dataKeys.length > 0);
+ if (scope.optDataKeys) {
+ ngModelCtrl.$setValidity('datasourceKeys', true);
+ } else {
+ ngModelCtrl.$setValidity('datasourceKeys',
+ angular.isDefined(value.dataKeys) &&
+ value.dataKeys != null &&
+ value.dataKeys.length > 0);
+ }
}
}
};
@@ -222,6 +226,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document,
scope: {
widgetType: '=',
maxDataKeys: '=',
+ optDataKeys: '=',
generateDataKey: '&',
datakeySettingsSchema: '='
},
diff --git a/ui/src/app/components/datasource.directive.js b/ui/src/app/components/datasource.directive.js
index 42011b03a4..6d81553d89 100644
--- a/ui/src/app/components/datasource.directive.js
+++ b/ui/src/app/components/datasource.directive.js
@@ -83,6 +83,7 @@ function Datasource($compile, $templateCache, utils, types) {
scope: {
aliasController: '=',
maxDataKeys: '=',
+ optDataKeys: '=',
widgetType: '=',
functionsOnly: '=',
datakeySettingsSchema: '=',
diff --git a/ui/src/app/components/datasource.tpl.html b/ui/src/app/components/datasource.tpl.html
index 41b18dbb51..df0b1500a5 100644
--- a/ui/src/app/components/datasource.tpl.html
+++ b/ui/src/app/components/datasource.tpl.html
@@ -28,6 +28,7 @@
ng-switch-default
ng-model="model"
max-data-keys="maxDataKeys"
+ opt-data-keys="optDataKeys"
datakey-settings-schema="datakeySettingsSchema"
ng-required="model.type === types.datasourceType.function"
widget-type="widgetType"
@@ -36,6 +37,7 @@
{{ 'widget-action.open-right-layout' | translate }}
- {{ 'widget-action.set-entity-from-widget' | translate }}
-
+
+ {{ 'widget-action.set-entity-from-widget' | translate }}
+
+
+
+
+
+
-
+
{{stateControllerId}}
diff --git a/ui/src/app/dashboard/states/default-state-controller.js b/ui/src/app/dashboard/states/default-state-controller.js
index d39478f14c..ae92d37518 100644
--- a/ui/src/app/dashboard/states/default-state-controller.js
+++ b/ui/src/app/dashboard/states/default-state-controller.js
@@ -27,6 +27,7 @@ export default function DefaultStateController($scope, $location, $state, $state
vm.getStateId = getStateId;
vm.getStateParams = getStateParams;
vm.getStateParamsByStateId = getStateParamsByStateId;
+ vm.getEntityId = getEntityId;
vm.getStateName = getStateName;
@@ -103,6 +104,10 @@ export default function DefaultStateController($scope, $location, $state, $state
}
}
+ function getEntityId() {
+ return null;
+ }
+
function getStateObjById(id) {
for (var i=0; i < vm.stateObject.length; i++) {
if (vm.stateObject[i].id === id) {
diff --git a/ui/src/app/dashboard/states/entity-state-controller.js b/ui/src/app/dashboard/states/entity-state-controller.js
index 4510449e28..cbe91faaee 100644
--- a/ui/src/app/dashboard/states/entity-state-controller.js
+++ b/ui/src/app/dashboard/states/entity-state-controller.js
@@ -29,6 +29,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
vm.getStateId = getStateId;
vm.getStateParams = getStateParams;
vm.getStateParamsByStateId = getStateParamsByStateId;
+ vm.getEntityId = getEntityId;
vm.getStateName = getStateName;
@@ -111,6 +112,16 @@ export default function EntityStateController($scope, $location, $state, $stateP
}
}
+ function getEntityId(entityParamName) {
+ var stateParams = getStateParams();
+ if (!entityParamName || !entityParamName.length) {
+ return stateParams.entityId;
+ } else if (stateParams[entityParamName]) {
+ return stateParams[entityParamName].entityId;
+ }
+ return null;
+ }
+
function getStateObjById(id) {
for (var i=0; i < vm.stateObject.length; i++) {
if (vm.stateObject[i].id === id) {
@@ -135,15 +146,22 @@ export default function EntityStateController($scope, $location, $state, $stateP
function resolveEntity(params) {
var deferred = $q.defer();
if (params && params.entityId && params.entityId.id && params.entityId.entityType) {
- entityService.getEntity(params.entityId.entityType, params.entityId.id, {ignoreLoading: true, ignoreErrors: true}).then(
- function success(entity) {
- var entityName = entity.name;
- deferred.resolve(entityName);
- },
- function fail() {
- deferred.reject();
- }
- );
+ if (params.entityName && params.entityName.length) {
+ deferred.resolve(params.entityName);
+ } else {
+ entityService.getEntity(params.entityId.entityType, params.entityId.id, {
+ ignoreLoading: true,
+ ignoreErrors: true
+ }).then(
+ function success(entity) {
+ var entityName = entity.name;
+ deferred.resolve(entityName);
+ },
+ function fail() {
+ deferred.reject();
+ }
+ );
+ }
} else {
deferred.reject();
}
diff --git a/ui/src/app/dashboard/states/states-component.directive.js b/ui/src/app/dashboard/states/states-component.directive.js
index 538c3e9620..9af5122a0f 100644
--- a/ui/src/app/dashboard/states/states-component.directive.js
+++ b/ui/src/app/dashboard/states/states-component.directive.js
@@ -70,6 +70,15 @@ export default function StatesComponent($compile, $templateCache, $controller, s
return null;
}
}
+
+ stateController.getEntityId = function(entityParamName) {
+ if (scope.statesController) {
+ return scope.statesController.getEntityId(entityParamName);
+ } else {
+ return null;
+ }
+ }
+
}
scope.$on('$destroy', function callOnDestroyHook() {
diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js
index b81f36941d..db7a744a60 100644
--- a/ui/src/app/entity/entity-filter.directive.js
+++ b/ui/src/app/entity/entity-filter.directive.js
@@ -54,6 +54,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
filter.entityNameFilter = '';
break;
case types.aliasFilterType.stateEntity.value:
+ filter.stateEntityParamName = null;
break;
case types.aliasFilterType.assetType.value:
filter.assetType = null;
@@ -67,6 +68,7 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc
case types.aliasFilterType.assetSearchQuery.value:
case types.aliasFilterType.deviceSearchQuery.value:
filter.rootStateEntity = false;
+ filter.stateEntityParamName = null;
filter.rootEntity = null;
filter.direction = types.entitySearchDirection.from;
filter.maxLevel = 1;
diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html
index 4f5af00bf2..dae28172af 100644
--- a/ui/src/app/entity/entity-filter.tpl.html
+++ b/ui/src/app/entity/entity-filter.tpl.html
@@ -59,6 +59,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/app/locale/locale.constant.js b/ui/src/app/locale/locale.constant.js
index f1f8543267..2a33d96b2c 100644
--- a/ui/src/app/locale/locale.constant.js
+++ b/ui/src/app/locale/locale.constant.js
@@ -187,6 +187,8 @@ export default angular.module('thingsboard.locale', [])
"no-entity-filter-specified": "No entity filter specified",
"root-state-entity": "Use dashboard state entity as root",
"root-entity": "Root entity",
+ "state-entity-parameter-name": "State entity parameter name",
+ "default-entity-parameter-name": "By default",
"max-relation-level": "Max relation level",
"unlimited-level": "Unlimited level",
"state-entity": "Dashboard state entity",
diff --git a/ui/src/app/widget/lib/entities-table-widget.js b/ui/src/app/widget/lib/entities-table-widget.js
index 2f8003756e..f0b5835fbe 100644
--- a/ui/src/app/widget/lib/entities-table-widget.js
+++ b/ui/src/app/widget/lib/entities-table-widget.js
@@ -479,6 +479,9 @@ function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $tra
for (var i=0;i