Commit 5626c30f2f7d64f8838118a2a3db9e83f98ea933
Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature/persisted-rpc
# Conflicts: # common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
Showing
57 changed files
with
3573 additions
and
304 deletions
Too many changes to show.
To preserve performance only 57 of 139 files are displayed.
... | ... | @@ -223,6 +223,27 @@ |
223 | 223 | "funcBody": null, |
224 | 224 | "usePostProcessing": null, |
225 | 225 | "postFuncBody": null |
226 | + }, | |
227 | + { | |
228 | + "name": "fw_url", | |
229 | + "type": "attribute", | |
230 | + "label": "fw_url", | |
231 | + "color": "#e91e63", | |
232 | + "settings": { | |
233 | + "columnWidth": "0px", | |
234 | + "useCellStyleFunction": false, | |
235 | + "cellStyleFunction": "", | |
236 | + "useCellContentFunction": false, | |
237 | + "cellContentFunction": "", | |
238 | + "defaultColumnVisibility": "hidden", | |
239 | + "columnSelectionToDisplay": "disabled" | |
240 | + }, | |
241 | + "_hash": 0.4204673738685043, | |
242 | + "units": null, | |
243 | + "decimals": null, | |
244 | + "funcBody": null, | |
245 | + "usePostProcessing": null, | |
246 | + "postFuncBody": null | |
226 | 247 | } |
227 | 248 | ] |
228 | 249 | } |
... | ... | @@ -249,23 +270,23 @@ |
249 | 270 | "icon": "edit", |
250 | 271 | "type": "customPretty", |
251 | 272 | "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", |
252 | - "customCss": "", | |
273 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
253 | 274 | "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", |
254 | 275 | "customResources": [], |
255 | 276 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
256 | 277 | }, |
257 | 278 | { |
258 | - "name": "Download firware", | |
279 | + "name": "Download firmware", | |
259 | 280 | "icon": "file_download", |
260 | 281 | "type": "custom", |
261 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
282 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
262 | 283 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
263 | 284 | }, |
264 | 285 | { |
265 | - "name": "Copy checksum", | |
286 | + "name": "Copy checksum/URL", | |
266 | 287 | "icon": "content_copy", |
267 | 288 | "type": "custom", |
268 | - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
289 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
269 | 290 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
270 | 291 | } |
271 | 292 | ] |
... | ... | @@ -997,6 +1018,27 @@ |
997 | 1018 | "funcBody": null, |
998 | 1019 | "usePostProcessing": null, |
999 | 1020 | "postFuncBody": null |
1021 | + }, | |
1022 | + { | |
1023 | + "name": "fw_url", | |
1024 | + "type": "attribute", | |
1025 | + "label": "fw_url", | |
1026 | + "color": "#e91e63", | |
1027 | + "settings": { | |
1028 | + "columnWidth": "0px", | |
1029 | + "useCellStyleFunction": false, | |
1030 | + "cellStyleFunction": "", | |
1031 | + "useCellContentFunction": false, | |
1032 | + "cellContentFunction": "", | |
1033 | + "defaultColumnVisibility": "hidden", | |
1034 | + "columnSelectionToDisplay": "disabled" | |
1035 | + }, | |
1036 | + "_hash": 0.4204673738685043, | |
1037 | + "units": null, | |
1038 | + "decimals": null, | |
1039 | + "funcBody": null, | |
1040 | + "usePostProcessing": null, | |
1041 | + "postFuncBody": null | |
1000 | 1042 | } |
1001 | 1043 | ] |
1002 | 1044 | } |
... | ... | @@ -1023,23 +1065,23 @@ |
1023 | 1065 | "icon": "edit", |
1024 | 1066 | "type": "customPretty", |
1025 | 1067 | "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", |
1026 | - "customCss": "", | |
1068 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1027 | 1069 | "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", |
1028 | 1070 | "customResources": [], |
1029 | 1071 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1030 | 1072 | }, |
1031 | 1073 | { |
1032 | - "name": "Download firware", | |
1074 | + "name": "Download firmware", | |
1033 | 1075 | "icon": "file_download", |
1034 | 1076 | "type": "custom", |
1035 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1077 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1036 | 1078 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1037 | 1079 | }, |
1038 | 1080 | { |
1039 | - "name": "Copy checksum", | |
1081 | + "name": "Copy checksum/URL", | |
1040 | 1082 | "icon": "content_copy", |
1041 | 1083 | "type": "custom", |
1042 | - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1084 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1043 | 1085 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1044 | 1086 | } |
1045 | 1087 | ] |
... | ... | @@ -1273,6 +1315,27 @@ |
1273 | 1315 | "funcBody": null, |
1274 | 1316 | "usePostProcessing": null, |
1275 | 1317 | "postFuncBody": null |
1318 | + }, | |
1319 | + { | |
1320 | + "name": "fw_url", | |
1321 | + "type": "attribute", | |
1322 | + "label": "fw_url", | |
1323 | + "color": "#e91e63", | |
1324 | + "settings": { | |
1325 | + "columnWidth": "0px", | |
1326 | + "useCellStyleFunction": false, | |
1327 | + "cellStyleFunction": "", | |
1328 | + "useCellContentFunction": false, | |
1329 | + "cellContentFunction": "", | |
1330 | + "defaultColumnVisibility": "hidden", | |
1331 | + "columnSelectionToDisplay": "disabled" | |
1332 | + }, | |
1333 | + "_hash": 0.4204673738685043, | |
1334 | + "units": null, | |
1335 | + "decimals": null, | |
1336 | + "funcBody": null, | |
1337 | + "usePostProcessing": null, | |
1338 | + "postFuncBody": null | |
1276 | 1339 | } |
1277 | 1340 | ] |
1278 | 1341 | } |
... | ... | @@ -1299,23 +1362,23 @@ |
1299 | 1362 | "icon": "edit", |
1300 | 1363 | "type": "customPretty", |
1301 | 1364 | "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", |
1302 | - "customCss": "", | |
1365 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1303 | 1366 | "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", |
1304 | 1367 | "customResources": [], |
1305 | 1368 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1306 | 1369 | }, |
1307 | 1370 | { |
1308 | - "name": "Download firware", | |
1371 | + "name": "Download firmware", | |
1309 | 1372 | "icon": "file_download", |
1310 | 1373 | "type": "custom", |
1311 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1374 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1312 | 1375 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1313 | 1376 | }, |
1314 | 1377 | { |
1315 | - "name": "Copy checksum", | |
1378 | + "name": "Copy checksum/URL", | |
1316 | 1379 | "icon": "content_copy", |
1317 | 1380 | "type": "custom", |
1318 | - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1381 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1319 | 1382 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1320 | 1383 | } |
1321 | 1384 | ] |
... | ... | @@ -1549,6 +1612,27 @@ |
1549 | 1612 | "funcBody": null, |
1550 | 1613 | "usePostProcessing": null, |
1551 | 1614 | "postFuncBody": null |
1615 | + }, | |
1616 | + { | |
1617 | + "name": "fw_url", | |
1618 | + "type": "attribute", | |
1619 | + "label": "fw_url", | |
1620 | + "color": "#e91e63", | |
1621 | + "settings": { | |
1622 | + "columnWidth": "0px", | |
1623 | + "useCellStyleFunction": false, | |
1624 | + "cellStyleFunction": "", | |
1625 | + "useCellContentFunction": false, | |
1626 | + "cellContentFunction": "", | |
1627 | + "defaultColumnVisibility": "hidden", | |
1628 | + "columnSelectionToDisplay": "disabled" | |
1629 | + }, | |
1630 | + "_hash": 0.4204673738685043, | |
1631 | + "units": null, | |
1632 | + "decimals": null, | |
1633 | + "funcBody": null, | |
1634 | + "usePostProcessing": null, | |
1635 | + "postFuncBody": null | |
1552 | 1636 | } |
1553 | 1637 | ] |
1554 | 1638 | } |
... | ... | @@ -1575,23 +1659,23 @@ |
1575 | 1659 | "icon": "edit", |
1576 | 1660 | "type": "customPretty", |
1577 | 1661 | "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", |
1578 | - "customCss": "", | |
1662 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1579 | 1663 | "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", |
1580 | 1664 | "customResources": [], |
1581 | 1665 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1582 | 1666 | }, |
1583 | 1667 | { |
1584 | - "name": "Download firware", | |
1668 | + "name": "Download firmware", | |
1585 | 1669 | "icon": "file_download", |
1586 | 1670 | "type": "custom", |
1587 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1671 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1588 | 1672 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1589 | 1673 | }, |
1590 | 1674 | { |
1591 | - "name": "Copy checksum", | |
1675 | + "name": "Copy checksum/URL", | |
1592 | 1676 | "icon": "content_copy", |
1593 | 1677 | "type": "custom", |
1594 | - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1678 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1595 | 1679 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1596 | 1680 | } |
1597 | 1681 | ] |
... | ... | @@ -1825,6 +1909,27 @@ |
1825 | 1909 | "funcBody": null, |
1826 | 1910 | "usePostProcessing": null, |
1827 | 1911 | "postFuncBody": null |
1912 | + }, | |
1913 | + { | |
1914 | + "name": "fw_url", | |
1915 | + "type": "attribute", | |
1916 | + "label": "fw_url", | |
1917 | + "color": "#e91e63", | |
1918 | + "settings": { | |
1919 | + "columnWidth": "0px", | |
1920 | + "useCellStyleFunction": false, | |
1921 | + "cellStyleFunction": "", | |
1922 | + "useCellContentFunction": false, | |
1923 | + "cellContentFunction": "", | |
1924 | + "defaultColumnVisibility": "hidden", | |
1925 | + "columnSelectionToDisplay": "disabled" | |
1926 | + }, | |
1927 | + "_hash": 0.4204673738685043, | |
1928 | + "units": null, | |
1929 | + "decimals": null, | |
1930 | + "funcBody": null, | |
1931 | + "usePostProcessing": null, | |
1932 | + "postFuncBody": null | |
1828 | 1933 | } |
1829 | 1934 | ] |
1830 | 1935 | } |
... | ... | @@ -1851,23 +1956,23 @@ |
1851 | 1956 | "icon": "edit", |
1852 | 1957 | "type": "customPretty", |
1853 | 1958 | "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", |
1854 | - "customCss": "", | |
1959 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1855 | 1960 | "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", |
1856 | 1961 | "customResources": [], |
1857 | 1962 | "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" |
1858 | 1963 | }, |
1859 | 1964 | { |
1860 | - "name": "Download firware", | |
1965 | + "name": "Download firmware", | |
1861 | 1966 | "icon": "file_download", |
1862 | 1967 | "type": "custom", |
1863 | - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", | |
1968 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1864 | 1969 | "id": "12533058-42f6-e75f-620c-219c48d01ec0" |
1865 | 1970 | }, |
1866 | 1971 | { |
1867 | - "name": "Copy checksum", | |
1972 | + "name": "Copy checksum/URL", | |
1868 | 1973 | "icon": "content_copy", |
1869 | 1974 | "type": "custom", |
1870 | - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", | |
1975 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", | |
1871 | 1976 | "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" |
1872 | 1977 | } |
1873 | 1978 | ] |
... | ... | @@ -1936,7 +2041,7 @@ |
1936 | 2041 | } |
1937 | 2042 | }, |
1938 | 2043 | "device_firmware_history": { |
1939 | - "name": "Device Firmware history", | |
2044 | + "name": "Firmware history: ${entityName}", | |
1940 | 2045 | "root": false, |
1941 | 2046 | "layouts": { |
1942 | 2047 | "main": { |
... | ... | @@ -2379,7 +2484,8 @@ |
2379 | 2484 | "titleColor": "rgba(0,0,0,0.870588)", |
2380 | 2485 | "showFilters": true, |
2381 | 2486 | "showDashboardLogo": false, |
2382 | - "dashboardLogoUrl": null | |
2487 | + "dashboardLogoUrl": null, | |
2488 | + "showUpdateDashboardImage": false | |
2383 | 2489 | } |
2384 | 2490 | }, |
2385 | 2491 | "name": "Firmware" | ... | ... |
1 | +{ | |
2 | + "title": "Software", | |
3 | + "image": null, | |
4 | + "configuration": { | |
5 | + "description": "", | |
6 | + "widgets": { | |
7 | + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { | |
8 | + "isSystemType": true, | |
9 | + "bundleAlias": "cards", | |
10 | + "typeAlias": "entities_table", | |
11 | + "type": "latest", | |
12 | + "title": "New widget", | |
13 | + "image": null, | |
14 | + "description": null, | |
15 | + "sizeX": 7.5, | |
16 | + "sizeY": 6.5, | |
17 | + "config": { | |
18 | + "timewindow": { | |
19 | + "realtime": { | |
20 | + "interval": 1000, | |
21 | + "timewindowMs": 86400000 | |
22 | + }, | |
23 | + "aggregation": { | |
24 | + "type": "NONE", | |
25 | + "limit": 200 | |
26 | + } | |
27 | + }, | |
28 | + "showTitle": true, | |
29 | + "backgroundColor": "rgb(255, 255, 255)", | |
30 | + "color": "rgba(0, 0, 0, 0.87)", | |
31 | + "padding": "4px", | |
32 | + "settings": { | |
33 | + "enableSearch": true, | |
34 | + "displayPagination": true, | |
35 | + "defaultPageSize": 10, | |
36 | + "defaultSortOrder": "entityLabel", | |
37 | + "displayEntityName": false, | |
38 | + "displayEntityType": false, | |
39 | + "enableSelectColumnDisplay": false, | |
40 | + "enableStickyHeader": true, | |
41 | + "enableStickyAction": false, | |
42 | + "entitiesTitle": "Devices", | |
43 | + "displayEntityLabel": true, | |
44 | + "entityLabelColumnTitle": "Device" | |
45 | + }, | |
46 | + "title": "New Entities table", | |
47 | + "dropShadow": true, | |
48 | + "enableFullscreen": true, | |
49 | + "titleStyle": { | |
50 | + "fontSize": "16px", | |
51 | + "fontWeight": 400, | |
52 | + "padding": "5px 10px 5px 10px" | |
53 | + }, | |
54 | + "useDashboardTimewindow": false, | |
55 | + "showLegend": false, | |
56 | + "datasources": [ | |
57 | + { | |
58 | + "type": "entity", | |
59 | + "name": null, | |
60 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
61 | + "filterId": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", | |
62 | + "dataKeys": [ | |
63 | + { | |
64 | + "name": "current_sw_title", | |
65 | + "type": "timeseries", | |
66 | + "label": "Current SW title", | |
67 | + "color": "#2196f3", | |
68 | + "settings": { | |
69 | + "columnWidth": "0px", | |
70 | + "useCellStyleFunction": false, | |
71 | + "cellStyleFunction": "", | |
72 | + "useCellContentFunction": false, | |
73 | + "defaultColumnVisibility": "visible", | |
74 | + "columnSelectionToDisplay": "enabled" | |
75 | + }, | |
76 | + "_hash": 0.09545533885166413, | |
77 | + "units": null, | |
78 | + "decimals": null, | |
79 | + "funcBody": null, | |
80 | + "usePostProcessing": null, | |
81 | + "postFuncBody": null | |
82 | + }, | |
83 | + { | |
84 | + "name": "current_sw_version", | |
85 | + "type": "timeseries", | |
86 | + "label": "Current SW version", | |
87 | + "color": "#4caf50", | |
88 | + "settings": { | |
89 | + "columnWidth": "0px", | |
90 | + "useCellStyleFunction": false, | |
91 | + "cellStyleFunction": "", | |
92 | + "useCellContentFunction": false, | |
93 | + "defaultColumnVisibility": "visible", | |
94 | + "columnSelectionToDisplay": "enabled" | |
95 | + }, | |
96 | + "_hash": 0.7206056602328659, | |
97 | + "units": null, | |
98 | + "decimals": null, | |
99 | + "funcBody": null, | |
100 | + "usePostProcessing": null, | |
101 | + "postFuncBody": null | |
102 | + }, | |
103 | + { | |
104 | + "name": "target_sw_title", | |
105 | + "type": "timeseries", | |
106 | + "label": "Target SW title", | |
107 | + "color": "#ffc107", | |
108 | + "settings": { | |
109 | + "columnWidth": "0px", | |
110 | + "useCellStyleFunction": false, | |
111 | + "cellStyleFunction": "", | |
112 | + "useCellContentFunction": false, | |
113 | + "defaultColumnVisibility": "visible", | |
114 | + "columnSelectionToDisplay": "enabled" | |
115 | + }, | |
116 | + "_hash": 0.9934225682766313, | |
117 | + "units": null, | |
118 | + "decimals": null, | |
119 | + "funcBody": null, | |
120 | + "usePostProcessing": null, | |
121 | + "postFuncBody": null | |
122 | + }, | |
123 | + { | |
124 | + "name": "target_sw_version", | |
125 | + "type": "timeseries", | |
126 | + "label": "Target SW version", | |
127 | + "color": "#607d8b", | |
128 | + "settings": { | |
129 | + "columnWidth": "0px", | |
130 | + "useCellStyleFunction": false, | |
131 | + "cellStyleFunction": "", | |
132 | + "useCellContentFunction": false, | |
133 | + "cellContentFunction": "", | |
134 | + "defaultColumnVisibility": "visible", | |
135 | + "columnSelectionToDisplay": "enabled" | |
136 | + }, | |
137 | + "_hash": 0.5251724416842531, | |
138 | + "units": null, | |
139 | + "decimals": null, | |
140 | + "funcBody": null, | |
141 | + "usePostProcessing": null, | |
142 | + "postFuncBody": null | |
143 | + }, | |
144 | + { | |
145 | + "name": "target_sw_ts", | |
146 | + "type": "timeseries", | |
147 | + "label": "Target SW set time", | |
148 | + "color": "#e91e63", | |
149 | + "settings": { | |
150 | + "columnWidth": "0px", | |
151 | + "useCellStyleFunction": false, | |
152 | + "cellStyleFunction": "", | |
153 | + "useCellContentFunction": true, | |
154 | + "defaultColumnVisibility": "visible", | |
155 | + "columnSelectionToDisplay": "enabled", | |
156 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
157 | + }, | |
158 | + "_hash": 0.31823244858578237, | |
159 | + "units": null, | |
160 | + "decimals": null, | |
161 | + "funcBody": null, | |
162 | + "usePostProcessing": null, | |
163 | + "postFuncBody": null | |
164 | + }, | |
165 | + { | |
166 | + "name": "sw_state", | |
167 | + "type": "timeseries", | |
168 | + "label": "Progress", | |
169 | + "color": "#9c27b0", | |
170 | + "settings": { | |
171 | + "columnWidth": "30%", | |
172 | + "useCellStyleFunction": true, | |
173 | + "useCellContentFunction": true, | |
174 | + "defaultColumnVisibility": "visible", | |
175 | + "columnSelectionToDisplay": "enabled", | |
176 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
177 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
178 | + }, | |
179 | + "_hash": 0.8174211757846257, | |
180 | + "units": null, | |
181 | + "decimals": null, | |
182 | + "funcBody": null, | |
183 | + "usePostProcessing": null, | |
184 | + "postFuncBody": null | |
185 | + }, | |
186 | + { | |
187 | + "name": "sw_state", | |
188 | + "type": "timeseries", | |
189 | + "label": "Status", | |
190 | + "color": "#f44336", | |
191 | + "settings": { | |
192 | + "columnWidth": "130px", | |
193 | + "useCellStyleFunction": true, | |
194 | + "useCellContentFunction": true, | |
195 | + "defaultColumnVisibility": "visible", | |
196 | + "columnSelectionToDisplay": "enabled", | |
197 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
198 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
199 | + }, | |
200 | + "_hash": 0.7764426948615217, | |
201 | + "units": null, | |
202 | + "decimals": null, | |
203 | + "funcBody": null, | |
204 | + "usePostProcessing": null, | |
205 | + "postFuncBody": null | |
206 | + }, | |
207 | + { | |
208 | + "name": "sw_checksum", | |
209 | + "type": "attribute", | |
210 | + "label": "sw_checksum", | |
211 | + "color": "#3f51b5", | |
212 | + "settings": { | |
213 | + "columnWidth": "0px", | |
214 | + "useCellStyleFunction": false, | |
215 | + "cellStyleFunction": "", | |
216 | + "useCellContentFunction": false, | |
217 | + "defaultColumnVisibility": "hidden", | |
218 | + "columnSelectionToDisplay": "disabled" | |
219 | + }, | |
220 | + "_hash": 0.5594087842471693, | |
221 | + "units": null, | |
222 | + "decimals": null, | |
223 | + "funcBody": null, | |
224 | + "usePostProcessing": null, | |
225 | + "postFuncBody": null | |
226 | + }, | |
227 | + { | |
228 | + "name": "sw_url", | |
229 | + "type": "attribute", | |
230 | + "label": "sw_url", | |
231 | + "color": "#e91e63", | |
232 | + "settings": { | |
233 | + "columnWidth": "0px", | |
234 | + "useCellStyleFunction": false, | |
235 | + "cellStyleFunction": "", | |
236 | + "useCellContentFunction": false, | |
237 | + "cellContentFunction": "", | |
238 | + "defaultColumnVisibility": "hidden", | |
239 | + "columnSelectionToDisplay": "disabled" | |
240 | + }, | |
241 | + "_hash": 0.3355829384124256, | |
242 | + "units": null, | |
243 | + "decimals": null, | |
244 | + "funcBody": null, | |
245 | + "usePostProcessing": null, | |
246 | + "postFuncBody": null | |
247 | + } | |
248 | + ] | |
249 | + } | |
250 | + ], | |
251 | + "actions": { | |
252 | + "actionCellButton": [ | |
253 | + { | |
254 | + "name": "History software update", | |
255 | + "icon": "history", | |
256 | + "type": "openDashboardState", | |
257 | + "targetDashboardStateId": "device_software_history", | |
258 | + "setEntityId": true, | |
259 | + "stateEntityParamName": null, | |
260 | + "openInSeparateDialog": false, | |
261 | + "dialogTitle": "", | |
262 | + "dialogHideDashboardToolbar": true, | |
263 | + "dialogWidth": null, | |
264 | + "dialogHeight": null, | |
265 | + "openRightLayout": false, | |
266 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
267 | + }, | |
268 | + { | |
269 | + "name": "Edit software", | |
270 | + "icon": "edit", | |
271 | + "type": "customPretty", | |
272 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
273 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
274 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
275 | + "customResources": [], | |
276 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
277 | + }, | |
278 | + { | |
279 | + "name": "Download software", | |
280 | + "icon": "file_download", | |
281 | + "type": "custom", | |
282 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
283 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
284 | + }, | |
285 | + { | |
286 | + "name": "Copy checksum/URL", | |
287 | + "icon": "content_copy", | |
288 | + "type": "custom", | |
289 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
290 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
291 | + } | |
292 | + ] | |
293 | + }, | |
294 | + "showTitleIcon": false, | |
295 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
296 | + "iconSize": "24px", | |
297 | + "titleTooltip": "", | |
298 | + "widgetStyle": {} | |
299 | + }, | |
300 | + "row": 0, | |
301 | + "col": 0, | |
302 | + "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016" | |
303 | + }, | |
304 | + "100b756c-0082-6505-3ae1-3603e6deea48": { | |
305 | + "isSystemType": true, | |
306 | + "bundleAlias": "cards", | |
307 | + "typeAlias": "timeseries_table", | |
308 | + "type": "timeseries", | |
309 | + "title": "New widget", | |
310 | + "image": null, | |
311 | + "description": null, | |
312 | + "sizeX": 8, | |
313 | + "sizeY": 6.5, | |
314 | + "config": { | |
315 | + "datasources": [ | |
316 | + { | |
317 | + "type": "entity", | |
318 | + "name": null, | |
319 | + "entityAliasId": "19f41c21-d9af-e666-8f50-e1748778f955", | |
320 | + "filterId": null, | |
321 | + "dataKeys": [ | |
322 | + { | |
323 | + "name": "current_sw_title", | |
324 | + "type": "timeseries", | |
325 | + "label": "Current software title", | |
326 | + "color": "#2196f3", | |
327 | + "settings": { | |
328 | + "useCellStyleFunction": false, | |
329 | + "cellStyleFunction": "", | |
330 | + "useCellContentFunction": false, | |
331 | + "cellContentFunction": "" | |
332 | + }, | |
333 | + "_hash": 0.5978079905579401, | |
334 | + "units": null, | |
335 | + "decimals": null, | |
336 | + "funcBody": null, | |
337 | + "usePostProcessing": null, | |
338 | + "postFuncBody": null | |
339 | + }, | |
340 | + { | |
341 | + "name": "current_sw_version", | |
342 | + "type": "timeseries", | |
343 | + "label": "Current software version", | |
344 | + "color": "#4caf50", | |
345 | + "settings": { | |
346 | + "useCellStyleFunction": false, | |
347 | + "cellStyleFunction": "", | |
348 | + "useCellContentFunction": false, | |
349 | + "cellContentFunction": "" | |
350 | + }, | |
351 | + "_hash": 0.027392025058568192, | |
352 | + "units": null, | |
353 | + "decimals": null, | |
354 | + "funcBody": null, | |
355 | + "usePostProcessing": null, | |
356 | + "postFuncBody": null | |
357 | + }, | |
358 | + { | |
359 | + "name": "target_sw_title", | |
360 | + "type": "timeseries", | |
361 | + "label": "Target software title", | |
362 | + "color": "#f44336", | |
363 | + "settings": { | |
364 | + "useCellStyleFunction": false, | |
365 | + "cellStyleFunction": "", | |
366 | + "useCellContentFunction": false, | |
367 | + "cellContentFunction": "" | |
368 | + }, | |
369 | + "_hash": 0.9496350796287059, | |
370 | + "units": null, | |
371 | + "decimals": null, | |
372 | + "funcBody": null, | |
373 | + "usePostProcessing": null, | |
374 | + "postFuncBody": null | |
375 | + }, | |
376 | + { | |
377 | + "name": "target_sw_version", | |
378 | + "type": "timeseries", | |
379 | + "label": "Target software version", | |
380 | + "color": "#ffc107", | |
381 | + "settings": { | |
382 | + "useCellStyleFunction": false, | |
383 | + "cellStyleFunction": "", | |
384 | + "useCellContentFunction": false, | |
385 | + "cellContentFunction": "" | |
386 | + }, | |
387 | + "_hash": 0.6734152252264187, | |
388 | + "units": null, | |
389 | + "decimals": null, | |
390 | + "funcBody": null, | |
391 | + "usePostProcessing": null, | |
392 | + "postFuncBody": null | |
393 | + }, | |
394 | + { | |
395 | + "name": "sw_state", | |
396 | + "type": "timeseries", | |
397 | + "label": "Status", | |
398 | + "color": "#607d8b", | |
399 | + "settings": { | |
400 | + "useCellStyleFunction": false, | |
401 | + "cellStyleFunction": "", | |
402 | + "useCellContentFunction": false, | |
403 | + "cellContentFunction": "" | |
404 | + }, | |
405 | + "_hash": 0.2983399718643074, | |
406 | + "units": null, | |
407 | + "decimals": null, | |
408 | + "funcBody": null, | |
409 | + "usePostProcessing": true, | |
410 | + "postFuncBody": "function capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\nif (value !== '') {\n return capitalize(value);\n}\nreturn value;" | |
411 | + } | |
412 | + ] | |
413 | + } | |
414 | + ], | |
415 | + "timewindow": { | |
416 | + "hideInterval": false, | |
417 | + "hideAggregation": false, | |
418 | + "hideAggInterval": false, | |
419 | + "hideTimezone": false, | |
420 | + "selectedTab": 0, | |
421 | + "realtime": { | |
422 | + "realtimeType": 0, | |
423 | + "timewindowMs": 2592000000, | |
424 | + "quickInterval": "CURRENT_DAY", | |
425 | + "interval": 1000 | |
426 | + }, | |
427 | + "aggregation": { | |
428 | + "type": "NONE", | |
429 | + "limit": 200 | |
430 | + } | |
431 | + }, | |
432 | + "showTitle": false, | |
433 | + "backgroundColor": "rgb(255, 255, 255)", | |
434 | + "color": "rgba(0, 0, 0, 0.87)", | |
435 | + "padding": "8px", | |
436 | + "settings": { | |
437 | + "showTimestamp": true, | |
438 | + "displayPagination": true, | |
439 | + "defaultPageSize": 10, | |
440 | + "enableSearch": true, | |
441 | + "enableStickyHeader": true, | |
442 | + "enableStickyAction": true | |
443 | + }, | |
444 | + "title": "Software history", | |
445 | + "dropShadow": false, | |
446 | + "enableFullscreen": false, | |
447 | + "titleStyle": { | |
448 | + "fontSize": "16px", | |
449 | + "fontWeight": 400, | |
450 | + "padding": "5px 10px 5px 10px" | |
451 | + }, | |
452 | + "useDashboardTimewindow": false, | |
453 | + "showLegend": false, | |
454 | + "widgetStyle": {}, | |
455 | + "actions": {}, | |
456 | + "showTitleIcon": false, | |
457 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
458 | + "iconSize": "24px", | |
459 | + "displayTimewindow": true, | |
460 | + "titleTooltip": "" | |
461 | + }, | |
462 | + "row": 0, | |
463 | + "col": 0, | |
464 | + "id": "100b756c-0082-6505-3ae1-3603e6deea48" | |
465 | + }, | |
466 | + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { | |
467 | + "isSystemType": true, | |
468 | + "bundleAlias": "cards", | |
469 | + "typeAlias": "html_value_card", | |
470 | + "type": "latest", | |
471 | + "title": "New widget", | |
472 | + "sizeX": 8, | |
473 | + "sizeY": 3, | |
474 | + "config": { | |
475 | + "datasources": [ | |
476 | + { | |
477 | + "type": "entityCount", | |
478 | + "name": "", | |
479 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
480 | + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
481 | + "dataKeys": [ | |
482 | + { | |
483 | + "name": "count", | |
484 | + "type": "count", | |
485 | + "label": "waitingDevicesNumber", | |
486 | + "color": "#4caf50", | |
487 | + "settings": {}, | |
488 | + "_hash": 0.7404827038869322, | |
489 | + "units": null, | |
490 | + "decimals": null, | |
491 | + "funcBody": null, | |
492 | + "usePostProcessing": null, | |
493 | + "postFuncBody": null | |
494 | + } | |
495 | + ] | |
496 | + } | |
497 | + ], | |
498 | + "timewindow": { | |
499 | + "realtime": { | |
500 | + "timewindowMs": 60000 | |
501 | + } | |
502 | + }, | |
503 | + "showTitle": false, | |
504 | + "backgroundColor": "#fff", | |
505 | + "color": "rgba(0, 0, 0, 0.87)", | |
506 | + "padding": "0px", | |
507 | + "settings": { | |
508 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <img id=\"activeDevices\" src='data:image/svg+xml;utf8,<svg width=\"24\" height=\"40\" viewBox=\"0 0 24 40\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M0 0V12H0.0200005L0 12.02L8 20L0 28L0.0200005 28.02H0V40H24V28.02H23.98L24 28L16 20L24 12.02L23.98 12H24V0H0ZM20 29V36H4V29L12 21L20 29ZM12 19L4 11V4H20V11L12 19Z\" fill=\"black\"/>\n</svg>\n'>\n <div class='value' id=\"activeDevices\">\n ${waitingDevicesNumber:0}\n </div> \n <div class='description' id=\"activeDevices\">\n Device Waiting\n </div>\n </div>\n</div>", | |
509 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
510 | + }, | |
511 | + "title": "New HTML Value Card", | |
512 | + "dropShadow": true, | |
513 | + "enableFullscreen": false, | |
514 | + "widgetStyle": {}, | |
515 | + "titleStyle": { | |
516 | + "fontSize": "16px", | |
517 | + "fontWeight": 400 | |
518 | + }, | |
519 | + "useDashboardTimewindow": true, | |
520 | + "showLegend": false, | |
521 | + "actions": { | |
522 | + "elementClick": [ | |
523 | + { | |
524 | + "name": "activeDevices", | |
525 | + "icon": "more_horiz", | |
526 | + "type": "openDashboardState", | |
527 | + "targetDashboardStateId": "device_waiting", | |
528 | + "setEntityId": false, | |
529 | + "stateEntityParamName": null, | |
530 | + "openInSeparateDialog": false, | |
531 | + "dialogTitle": "", | |
532 | + "dialogHideDashboardToolbar": true, | |
533 | + "dialogWidth": null, | |
534 | + "dialogHeight": null, | |
535 | + "openRightLayout": false, | |
536 | + "id": "4d9a77a2-f0a5-690c-a83b-b0e940be788c" | |
537 | + } | |
538 | + ] | |
539 | + }, | |
540 | + "showTitleIcon": false, | |
541 | + "titleIcon": null, | |
542 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
543 | + "iconSize": "24px", | |
544 | + "titleTooltip": "", | |
545 | + "enableDataExport": false, | |
546 | + "displayTimewindow": true | |
547 | + }, | |
548 | + "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6" | |
549 | + }, | |
550 | + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { | |
551 | + "isSystemType": true, | |
552 | + "bundleAlias": "cards", | |
553 | + "typeAlias": "html_value_card", | |
554 | + "type": "latest", | |
555 | + "title": "New widget", | |
556 | + "sizeX": 8, | |
557 | + "sizeY": 3, | |
558 | + "config": { | |
559 | + "datasources": [ | |
560 | + { | |
561 | + "type": "entityCount", | |
562 | + "name": "", | |
563 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
564 | + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
565 | + "dataKeys": [ | |
566 | + { | |
567 | + "name": "count", | |
568 | + "type": "count", | |
569 | + "label": "updatingDevicesNumber", | |
570 | + "color": "#4caf50", | |
571 | + "settings": {}, | |
572 | + "_hash": 0.7404827038869322, | |
573 | + "units": null, | |
574 | + "decimals": null, | |
575 | + "funcBody": null, | |
576 | + "usePostProcessing": null, | |
577 | + "postFuncBody": null | |
578 | + } | |
579 | + ] | |
580 | + } | |
581 | + ], | |
582 | + "timewindow": { | |
583 | + "realtime": { | |
584 | + "timewindowMs": 60000 | |
585 | + } | |
586 | + }, | |
587 | + "showTitle": false, | |
588 | + "backgroundColor": "#fff", | |
589 | + "color": "rgba(0, 0, 0, 0.87)", | |
590 | + "padding": "0px", | |
591 | + "settings": { | |
592 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <img id=\"activeDevices\" src='data:image/svg+xml;utf8,<svg width=\"36\" height=\"36\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M36 14.24H22.44L27.92 8.6C22.46 3.2 13.62 3 8.16001 8.4C2.70001 13.82 2.70001 22.56 8.16001 27.98C13.62 33.4 22.46 33.4 27.92 27.98C30.64 25.3 32 22.16 32 18.2H36C36 22.16 34.24 27.3 30.72 30.78C23.7 37.74 12.3 37.74 5.28001 30.78C-1.71999 23.84 -1.77999 12.56 5.24001 5.62C12.26 -1.32 23.52 -1.32 30.54 5.62L36 0V14.24ZM19 10V18.5L26 22.66L24.56 25.08L16 20V10H19Z\" fill=\"black\"/>\n</svg>'>\n <div class='value' id=\"activeDevices\">\n ${updatingDevicesNumber:0}\n </div> \n <div class='description' id=\"activeDevices\">\n Device Updating\n </div>\n </div>\n</div>", | |
593 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
594 | + }, | |
595 | + "title": "New HTML Value Card", | |
596 | + "dropShadow": true, | |
597 | + "enableFullscreen": false, | |
598 | + "widgetStyle": {}, | |
599 | + "titleStyle": { | |
600 | + "fontSize": "16px", | |
601 | + "fontWeight": 400 | |
602 | + }, | |
603 | + "useDashboardTimewindow": true, | |
604 | + "showLegend": false, | |
605 | + "actions": { | |
606 | + "elementClick": [ | |
607 | + { | |
608 | + "name": "activeDevices", | |
609 | + "icon": "more_horiz", | |
610 | + "type": "openDashboardState", | |
611 | + "targetDashboardStateId": "device_updating", | |
612 | + "setEntityId": false, | |
613 | + "stateEntityParamName": null, | |
614 | + "openInSeparateDialog": false, | |
615 | + "dialogTitle": "", | |
616 | + "dialogHideDashboardToolbar": true, | |
617 | + "dialogWidth": null, | |
618 | + "dialogHeight": null, | |
619 | + "openRightLayout": false, | |
620 | + "id": "57d39904-2350-b29b-78ed-56b8268814cb" | |
621 | + } | |
622 | + ] | |
623 | + }, | |
624 | + "showTitleIcon": false, | |
625 | + "titleIcon": null, | |
626 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
627 | + "iconSize": "24px", | |
628 | + "titleTooltip": "", | |
629 | + "enableDataExport": false, | |
630 | + "displayTimewindow": true | |
631 | + }, | |
632 | + "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6" | |
633 | + }, | |
634 | + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { | |
635 | + "isSystemType": true, | |
636 | + "bundleAlias": "cards", | |
637 | + "typeAlias": "html_value_card", | |
638 | + "type": "latest", | |
639 | + "title": "New widget", | |
640 | + "sizeX": 8, | |
641 | + "sizeY": 3, | |
642 | + "config": { | |
643 | + "datasources": [ | |
644 | + { | |
645 | + "type": "entityCount", | |
646 | + "name": "", | |
647 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
648 | + "filterId": "6044e198-df64-cd76-f339-696f220c4943", | |
649 | + "dataKeys": [ | |
650 | + { | |
651 | + "name": "count", | |
652 | + "type": "count", | |
653 | + "label": "updatedDevicesNumber", | |
654 | + "color": "#4caf50", | |
655 | + "settings": {}, | |
656 | + "_hash": 0.7404827038869322, | |
657 | + "units": null, | |
658 | + "decimals": null, | |
659 | + "funcBody": null, | |
660 | + "usePostProcessing": null, | |
661 | + "postFuncBody": null | |
662 | + } | |
663 | + ] | |
664 | + } | |
665 | + ], | |
666 | + "timewindow": { | |
667 | + "realtime": { | |
668 | + "timewindowMs": 60000 | |
669 | + } | |
670 | + }, | |
671 | + "showTitle": false, | |
672 | + "backgroundColor": "#fff", | |
673 | + "color": "rgba(0, 0, 0, 0.87)", | |
674 | + "padding": "0px", | |
675 | + "settings": { | |
676 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <img id=\"activeDevices\" src='data:image/svg+xml;utf8,<svg width=\"34\" height=\"32\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/>\n<path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/>\n</svg>'>\n <div class='value' id=\"activeDevices\">\n ${updatedDevicesNumber:0}\n </div> \n <div class='description' id=\"activeDevices\">\n Device Updated\n </div>\n </div>\n</div>", | |
677 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
678 | + }, | |
679 | + "title": "New HTML Value Card", | |
680 | + "dropShadow": true, | |
681 | + "enableFullscreen": false, | |
682 | + "widgetStyle": {}, | |
683 | + "titleStyle": { | |
684 | + "fontSize": "16px", | |
685 | + "fontWeight": 400 | |
686 | + }, | |
687 | + "useDashboardTimewindow": true, | |
688 | + "showLegend": false, | |
689 | + "actions": { | |
690 | + "elementClick": [ | |
691 | + { | |
692 | + "name": "activeDevices", | |
693 | + "icon": "more_horiz", | |
694 | + "type": "openDashboardState", | |
695 | + "targetDashboardStateId": "device_updated", | |
696 | + "setEntityId": false, | |
697 | + "stateEntityParamName": null, | |
698 | + "openInSeparateDialog": false, | |
699 | + "dialogTitle": "", | |
700 | + "dialogHideDashboardToolbar": true, | |
701 | + "dialogWidth": null, | |
702 | + "dialogHeight": null, | |
703 | + "openRightLayout": false, | |
704 | + "id": "d787c212-8c56-34f0-349a-5aae2ffd1eae" | |
705 | + } | |
706 | + ] | |
707 | + }, | |
708 | + "showTitleIcon": false, | |
709 | + "titleIcon": null, | |
710 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
711 | + "iconSize": "24px", | |
712 | + "titleTooltip": "", | |
713 | + "enableDataExport": false, | |
714 | + "displayTimewindow": true | |
715 | + }, | |
716 | + "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81" | |
717 | + }, | |
718 | + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { | |
719 | + "isSystemType": true, | |
720 | + "bundleAlias": "cards", | |
721 | + "typeAlias": "html_value_card", | |
722 | + "type": "latest", | |
723 | + "title": "New widget", | |
724 | + "sizeX": 8, | |
725 | + "sizeY": 3, | |
726 | + "config": { | |
727 | + "datasources": [ | |
728 | + { | |
729 | + "type": "entityCount", | |
730 | + "name": "", | |
731 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
732 | + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
733 | + "dataKeys": [ | |
734 | + { | |
735 | + "name": "count", | |
736 | + "type": "count", | |
737 | + "label": "updatingDevicesNumber", | |
738 | + "color": "#4caf50", | |
739 | + "settings": {}, | |
740 | + "_hash": 0.7404827038869322, | |
741 | + "units": null, | |
742 | + "decimals": null, | |
743 | + "funcBody": null, | |
744 | + "usePostProcessing": null, | |
745 | + "postFuncBody": null | |
746 | + } | |
747 | + ] | |
748 | + } | |
749 | + ], | |
750 | + "timewindow": { | |
751 | + "realtime": { | |
752 | + "timewindowMs": 60000 | |
753 | + } | |
754 | + }, | |
755 | + "showTitle": false, | |
756 | + "backgroundColor": "#fff", | |
757 | + "color": "rgba(0, 0, 0, 0.87)", | |
758 | + "padding": "0px", | |
759 | + "settings": { | |
760 | + "cardHtml": "<div class='card' id=\"activeDevices\">\n <div class='content' id=\"activeDevices\">\n <div class=\"container-svg\" id=\"activeDevices\">\n <svg viewBox=\"0 0 24 24\" id=\"activeDevices\">\n <path id=\"activeDevices\" fill=\"currentColor\" d=\"M13 14H11V9H13M13 18H11V16H13M1 21H23L12 2L1 21Z\" />\n </svg>\n </div>\n <div class='value error_software_failed_count' id=\"activeDevices\">\n ${updatingDevicesNumber:0}\n </div> \n <script type=\"text/javascript\">\n function init() {\n var counter = $('.error_software_failed_count');\n var value = +counter.text();\n if(value) {\n counter.css('color', '#D93025');\n }\n };\n init();\n </script>\n <div class='description' id=\"activeDevices\">\n Device Failed\n </div>\n </div>\n</div>", | |
761 | + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .container-svg {\n height: 40px;\n width: 40px;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .container-svg {\n height: 28px;\n width: 28px;\n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" | |
762 | + }, | |
763 | + "title": "New HTML Value Card", | |
764 | + "dropShadow": true, | |
765 | + "enableFullscreen": false, | |
766 | + "widgetStyle": {}, | |
767 | + "titleStyle": { | |
768 | + "fontSize": "16px", | |
769 | + "fontWeight": 400 | |
770 | + }, | |
771 | + "useDashboardTimewindow": true, | |
772 | + "showLegend": false, | |
773 | + "actions": { | |
774 | + "elementClick": [ | |
775 | + { | |
776 | + "name": "activeDevices", | |
777 | + "icon": "more_horiz", | |
778 | + "type": "openDashboardState", | |
779 | + "targetDashboardStateId": "device_error", | |
780 | + "setEntityId": false, | |
781 | + "stateEntityParamName": null, | |
782 | + "openInSeparateDialog": false, | |
783 | + "dialogTitle": "", | |
784 | + "dialogHideDashboardToolbar": true, | |
785 | + "dialogWidth": null, | |
786 | + "dialogHeight": null, | |
787 | + "openRightLayout": false, | |
788 | + "id": "0b3d2887-9929-84d5-3795-0763dca15cba" | |
789 | + } | |
790 | + ] | |
791 | + }, | |
792 | + "showTitleIcon": false, | |
793 | + "titleIcon": null, | |
794 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
795 | + "iconSize": "24px", | |
796 | + "titleTooltip": "", | |
797 | + "enableDataExport": false, | |
798 | + "displayTimewindow": true | |
799 | + }, | |
800 | + "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8" | |
801 | + }, | |
802 | + "21be08bb-ec90-f760-ad6f-e7678f12c401": { | |
803 | + "isSystemType": true, | |
804 | + "bundleAlias": "cards", | |
805 | + "typeAlias": "entities_table", | |
806 | + "type": "latest", | |
807 | + "title": "New widget", | |
808 | + "image": null, | |
809 | + "description": null, | |
810 | + "sizeX": 7.5, | |
811 | + "sizeY": 6.5, | |
812 | + "config": { | |
813 | + "timewindow": { | |
814 | + "realtime": { | |
815 | + "interval": 1000, | |
816 | + "timewindowMs": 86400000 | |
817 | + }, | |
818 | + "aggregation": { | |
819 | + "type": "NONE", | |
820 | + "limit": 200 | |
821 | + } | |
822 | + }, | |
823 | + "showTitle": true, | |
824 | + "backgroundColor": "rgb(255, 255, 255)", | |
825 | + "color": "rgba(0, 0, 0, 0.87)", | |
826 | + "padding": "4px", | |
827 | + "settings": { | |
828 | + "enableSearch": true, | |
829 | + "displayPagination": true, | |
830 | + "defaultPageSize": 10, | |
831 | + "defaultSortOrder": "entityLabel", | |
832 | + "displayEntityName": false, | |
833 | + "displayEntityType": false, | |
834 | + "enableSelectColumnDisplay": false, | |
835 | + "enableStickyHeader": true, | |
836 | + "enableStickyAction": true, | |
837 | + "entitiesTitle": "Devices", | |
838 | + "displayEntityLabel": true, | |
839 | + "entityLabelColumnTitle": "Device" | |
840 | + }, | |
841 | + "title": "New Entities table", | |
842 | + "dropShadow": true, | |
843 | + "enableFullscreen": true, | |
844 | + "titleStyle": { | |
845 | + "fontSize": "16px", | |
846 | + "fontWeight": 400, | |
847 | + "padding": "5px 10px 5px 10px" | |
848 | + }, | |
849 | + "useDashboardTimewindow": false, | |
850 | + "showLegend": false, | |
851 | + "datasources": [ | |
852 | + { | |
853 | + "type": "entity", | |
854 | + "name": null, | |
855 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
856 | + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
857 | + "dataKeys": [ | |
858 | + { | |
859 | + "name": "current_sw_title", | |
860 | + "type": "timeseries", | |
861 | + "label": "Current SW title", | |
862 | + "color": "#2196f3", | |
863 | + "settings": { | |
864 | + "columnWidth": "0px", | |
865 | + "useCellStyleFunction": false, | |
866 | + "cellStyleFunction": "", | |
867 | + "useCellContentFunction": false, | |
868 | + "defaultColumnVisibility": "visible", | |
869 | + "columnSelectionToDisplay": "enabled" | |
870 | + }, | |
871 | + "_hash": 0.09545533885166413, | |
872 | + "units": null, | |
873 | + "decimals": null, | |
874 | + "funcBody": null, | |
875 | + "usePostProcessing": null, | |
876 | + "postFuncBody": null | |
877 | + }, | |
878 | + { | |
879 | + "name": "current_sw_version", | |
880 | + "type": "timeseries", | |
881 | + "label": "Current SW version", | |
882 | + "color": "#4caf50", | |
883 | + "settings": { | |
884 | + "columnWidth": "0px", | |
885 | + "useCellStyleFunction": false, | |
886 | + "cellStyleFunction": "", | |
887 | + "useCellContentFunction": false, | |
888 | + "defaultColumnVisibility": "visible", | |
889 | + "columnSelectionToDisplay": "enabled" | |
890 | + }, | |
891 | + "_hash": 0.7206056602328659, | |
892 | + "units": null, | |
893 | + "decimals": null, | |
894 | + "funcBody": null, | |
895 | + "usePostProcessing": null, | |
896 | + "postFuncBody": null | |
897 | + }, | |
898 | + { | |
899 | + "name": "target_sw_title", | |
900 | + "type": "timeseries", | |
901 | + "label": "Target SW title", | |
902 | + "color": "#ffc107", | |
903 | + "settings": { | |
904 | + "columnWidth": "0px", | |
905 | + "useCellStyleFunction": false, | |
906 | + "cellStyleFunction": "", | |
907 | + "useCellContentFunction": false, | |
908 | + "defaultColumnVisibility": "visible", | |
909 | + "columnSelectionToDisplay": "enabled" | |
910 | + }, | |
911 | + "_hash": 0.9934225682766313, | |
912 | + "units": null, | |
913 | + "decimals": null, | |
914 | + "funcBody": null, | |
915 | + "usePostProcessing": null, | |
916 | + "postFuncBody": null | |
917 | + }, | |
918 | + { | |
919 | + "name": "target_sw_version", | |
920 | + "type": "timeseries", | |
921 | + "label": "Target SW version", | |
922 | + "color": "#607d8b", | |
923 | + "settings": { | |
924 | + "columnWidth": "0px", | |
925 | + "useCellStyleFunction": false, | |
926 | + "cellStyleFunction": "", | |
927 | + "useCellContentFunction": false, | |
928 | + "cellContentFunction": "", | |
929 | + "defaultColumnVisibility": "visible", | |
930 | + "columnSelectionToDisplay": "enabled" | |
931 | + }, | |
932 | + "_hash": 0.5251724416842531, | |
933 | + "units": null, | |
934 | + "decimals": null, | |
935 | + "funcBody": null, | |
936 | + "usePostProcessing": null, | |
937 | + "postFuncBody": null | |
938 | + }, | |
939 | + { | |
940 | + "name": "target_sw_ts", | |
941 | + "type": "timeseries", | |
942 | + "label": "Target SW set time", | |
943 | + "color": "#e91e63", | |
944 | + "settings": { | |
945 | + "columnWidth": "0px", | |
946 | + "useCellStyleFunction": false, | |
947 | + "cellStyleFunction": "", | |
948 | + "useCellContentFunction": true, | |
949 | + "defaultColumnVisibility": "visible", | |
950 | + "columnSelectionToDisplay": "enabled", | |
951 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
952 | + }, | |
953 | + "_hash": 0.31823244858578237, | |
954 | + "units": null, | |
955 | + "decimals": null, | |
956 | + "funcBody": null, | |
957 | + "usePostProcessing": null, | |
958 | + "postFuncBody": null | |
959 | + }, | |
960 | + { | |
961 | + "name": "sw_state", | |
962 | + "type": "timeseries", | |
963 | + "label": "Progress", | |
964 | + "color": "#9c27b0", | |
965 | + "settings": { | |
966 | + "columnWidth": "30%", | |
967 | + "useCellStyleFunction": true, | |
968 | + "useCellContentFunction": true, | |
969 | + "defaultColumnVisibility": "visible", | |
970 | + "columnSelectionToDisplay": "enabled", | |
971 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
972 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
973 | + }, | |
974 | + "_hash": 0.8174211757846257, | |
975 | + "units": null, | |
976 | + "decimals": null, | |
977 | + "funcBody": null, | |
978 | + "usePostProcessing": null, | |
979 | + "postFuncBody": null | |
980 | + }, | |
981 | + { | |
982 | + "name": "sw_state", | |
983 | + "type": "timeseries", | |
984 | + "label": "Status", | |
985 | + "color": "#f44336", | |
986 | + "settings": { | |
987 | + "columnWidth": "130px", | |
988 | + "useCellStyleFunction": true, | |
989 | + "useCellContentFunction": true, | |
990 | + "defaultColumnVisibility": "visible", | |
991 | + "columnSelectionToDisplay": "enabled", | |
992 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
993 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
994 | + }, | |
995 | + "_hash": 0.7764426948615217, | |
996 | + "units": null, | |
997 | + "decimals": null, | |
998 | + "funcBody": null, | |
999 | + "usePostProcessing": null, | |
1000 | + "postFuncBody": null | |
1001 | + }, | |
1002 | + { | |
1003 | + "name": "sw_checksum", | |
1004 | + "type": "attribute", | |
1005 | + "label": "sw_checksum", | |
1006 | + "color": "#3f51b5", | |
1007 | + "settings": { | |
1008 | + "columnWidth": "0px", | |
1009 | + "useCellStyleFunction": false, | |
1010 | + "cellStyleFunction": "", | |
1011 | + "useCellContentFunction": false, | |
1012 | + "defaultColumnVisibility": "hidden", | |
1013 | + "columnSelectionToDisplay": "disabled" | |
1014 | + }, | |
1015 | + "_hash": 0.5594087842471693, | |
1016 | + "units": null, | |
1017 | + "decimals": null, | |
1018 | + "funcBody": null, | |
1019 | + "usePostProcessing": null, | |
1020 | + "postFuncBody": null | |
1021 | + }, | |
1022 | + { | |
1023 | + "name": "sw_url", | |
1024 | + "type": "attribute", | |
1025 | + "label": "sw_url", | |
1026 | + "color": "#e91e63", | |
1027 | + "settings": { | |
1028 | + "columnWidth": "0px", | |
1029 | + "useCellStyleFunction": false, | |
1030 | + "cellStyleFunction": "", | |
1031 | + "useCellContentFunction": false, | |
1032 | + "cellContentFunction": "", | |
1033 | + "defaultColumnVisibility": "hidden", | |
1034 | + "columnSelectionToDisplay": "disabled" | |
1035 | + }, | |
1036 | + "_hash": 0.3355829384124256, | |
1037 | + "units": null, | |
1038 | + "decimals": null, | |
1039 | + "funcBody": null, | |
1040 | + "usePostProcessing": null, | |
1041 | + "postFuncBody": null | |
1042 | + } | |
1043 | + ] | |
1044 | + } | |
1045 | + ], | |
1046 | + "actions": { | |
1047 | + "actionCellButton": [ | |
1048 | + { | |
1049 | + "name": "History software update", | |
1050 | + "icon": "history", | |
1051 | + "type": "openDashboardState", | |
1052 | + "targetDashboardStateId": "device_software_history", | |
1053 | + "setEntityId": true, | |
1054 | + "stateEntityParamName": null, | |
1055 | + "openInSeparateDialog": false, | |
1056 | + "dialogTitle": "", | |
1057 | + "dialogHideDashboardToolbar": true, | |
1058 | + "dialogWidth": null, | |
1059 | + "dialogHeight": null, | |
1060 | + "openRightLayout": false, | |
1061 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1062 | + }, | |
1063 | + { | |
1064 | + "name": "Edit software", | |
1065 | + "icon": "edit", | |
1066 | + "type": "customPretty", | |
1067 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1068 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1069 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1070 | + "customResources": [], | |
1071 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1072 | + }, | |
1073 | + { | |
1074 | + "name": "Download software", | |
1075 | + "icon": "file_download", | |
1076 | + "type": "custom", | |
1077 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1078 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1079 | + }, | |
1080 | + { | |
1081 | + "name": "Copy checksum/URL", | |
1082 | + "icon": "content_copy", | |
1083 | + "type": "custom", | |
1084 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1085 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1086 | + } | |
1087 | + ] | |
1088 | + }, | |
1089 | + "showTitleIcon": false, | |
1090 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1091 | + "iconSize": "24px", | |
1092 | + "titleTooltip": "", | |
1093 | + "widgetStyle": {} | |
1094 | + }, | |
1095 | + "row": 0, | |
1096 | + "col": 0, | |
1097 | + "id": "21be08bb-ec90-f760-ad6f-e7678f12c401" | |
1098 | + }, | |
1099 | + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { | |
1100 | + "isSystemType": true, | |
1101 | + "bundleAlias": "cards", | |
1102 | + "typeAlias": "entities_table", | |
1103 | + "type": "latest", | |
1104 | + "title": "New widget", | |
1105 | + "image": null, | |
1106 | + "description": null, | |
1107 | + "sizeX": 7.5, | |
1108 | + "sizeY": 6.5, | |
1109 | + "config": { | |
1110 | + "timewindow": { | |
1111 | + "realtime": { | |
1112 | + "interval": 1000, | |
1113 | + "timewindowMs": 86400000 | |
1114 | + }, | |
1115 | + "aggregation": { | |
1116 | + "type": "NONE", | |
1117 | + "limit": 200 | |
1118 | + } | |
1119 | + }, | |
1120 | + "showTitle": true, | |
1121 | + "backgroundColor": "rgb(255, 255, 255)", | |
1122 | + "color": "rgba(0, 0, 0, 0.87)", | |
1123 | + "padding": "4px", | |
1124 | + "settings": { | |
1125 | + "enableSearch": true, | |
1126 | + "displayPagination": true, | |
1127 | + "defaultPageSize": 10, | |
1128 | + "defaultSortOrder": "entityLabel", | |
1129 | + "displayEntityName": false, | |
1130 | + "displayEntityType": false, | |
1131 | + "enableSelectColumnDisplay": false, | |
1132 | + "enableStickyHeader": true, | |
1133 | + "enableStickyAction": true, | |
1134 | + "entitiesTitle": "Devices", | |
1135 | + "displayEntityLabel": true, | |
1136 | + "entityLabelColumnTitle": "Device" | |
1137 | + }, | |
1138 | + "title": "New Entities table", | |
1139 | + "dropShadow": true, | |
1140 | + "enableFullscreen": true, | |
1141 | + "titleStyle": { | |
1142 | + "fontSize": "16px", | |
1143 | + "fontWeight": 400, | |
1144 | + "padding": "5px 10px 5px 10px" | |
1145 | + }, | |
1146 | + "useDashboardTimewindow": false, | |
1147 | + "showLegend": false, | |
1148 | + "datasources": [ | |
1149 | + { | |
1150 | + "type": "entity", | |
1151 | + "name": null, | |
1152 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1153 | + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
1154 | + "dataKeys": [ | |
1155 | + { | |
1156 | + "name": "current_sw_title", | |
1157 | + "type": "timeseries", | |
1158 | + "label": "Current SW title", | |
1159 | + "color": "#2196f3", | |
1160 | + "settings": { | |
1161 | + "columnWidth": "0px", | |
1162 | + "useCellStyleFunction": false, | |
1163 | + "cellStyleFunction": "", | |
1164 | + "useCellContentFunction": false, | |
1165 | + "defaultColumnVisibility": "visible", | |
1166 | + "columnSelectionToDisplay": "enabled" | |
1167 | + }, | |
1168 | + "_hash": 0.09545533885166413, | |
1169 | + "units": null, | |
1170 | + "decimals": null, | |
1171 | + "funcBody": null, | |
1172 | + "usePostProcessing": null, | |
1173 | + "postFuncBody": null | |
1174 | + }, | |
1175 | + { | |
1176 | + "name": "current_sw_version", | |
1177 | + "type": "timeseries", | |
1178 | + "label": "Current SW version", | |
1179 | + "color": "#4caf50", | |
1180 | + "settings": { | |
1181 | + "columnWidth": "0px", | |
1182 | + "useCellStyleFunction": false, | |
1183 | + "cellStyleFunction": "", | |
1184 | + "useCellContentFunction": false, | |
1185 | + "defaultColumnVisibility": "visible", | |
1186 | + "columnSelectionToDisplay": "enabled" | |
1187 | + }, | |
1188 | + "_hash": 0.7206056602328659, | |
1189 | + "units": null, | |
1190 | + "decimals": null, | |
1191 | + "funcBody": null, | |
1192 | + "usePostProcessing": null, | |
1193 | + "postFuncBody": null | |
1194 | + }, | |
1195 | + { | |
1196 | + "name": "target_sw_title", | |
1197 | + "type": "timeseries", | |
1198 | + "label": "Target SW title", | |
1199 | + "color": "#ffc107", | |
1200 | + "settings": { | |
1201 | + "columnWidth": "0px", | |
1202 | + "useCellStyleFunction": false, | |
1203 | + "cellStyleFunction": "", | |
1204 | + "useCellContentFunction": false, | |
1205 | + "defaultColumnVisibility": "visible", | |
1206 | + "columnSelectionToDisplay": "enabled" | |
1207 | + }, | |
1208 | + "_hash": 0.9934225682766313, | |
1209 | + "units": null, | |
1210 | + "decimals": null, | |
1211 | + "funcBody": null, | |
1212 | + "usePostProcessing": null, | |
1213 | + "postFuncBody": null | |
1214 | + }, | |
1215 | + { | |
1216 | + "name": "target_sw_version", | |
1217 | + "type": "timeseries", | |
1218 | + "label": "Target SW version", | |
1219 | + "color": "#607d8b", | |
1220 | + "settings": { | |
1221 | + "columnWidth": "0px", | |
1222 | + "useCellStyleFunction": false, | |
1223 | + "cellStyleFunction": "", | |
1224 | + "useCellContentFunction": false, | |
1225 | + "cellContentFunction": "", | |
1226 | + "defaultColumnVisibility": "visible", | |
1227 | + "columnSelectionToDisplay": "enabled" | |
1228 | + }, | |
1229 | + "_hash": 0.5251724416842531, | |
1230 | + "units": null, | |
1231 | + "decimals": null, | |
1232 | + "funcBody": null, | |
1233 | + "usePostProcessing": null, | |
1234 | + "postFuncBody": null | |
1235 | + }, | |
1236 | + { | |
1237 | + "name": "target_sw_ts", | |
1238 | + "type": "timeseries", | |
1239 | + "label": "Target SW set time", | |
1240 | + "color": "#e91e63", | |
1241 | + "settings": { | |
1242 | + "columnWidth": "0px", | |
1243 | + "useCellStyleFunction": false, | |
1244 | + "cellStyleFunction": "", | |
1245 | + "useCellContentFunction": true, | |
1246 | + "defaultColumnVisibility": "visible", | |
1247 | + "columnSelectionToDisplay": "enabled", | |
1248 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1249 | + }, | |
1250 | + "_hash": 0.31823244858578237, | |
1251 | + "units": null, | |
1252 | + "decimals": null, | |
1253 | + "funcBody": null, | |
1254 | + "usePostProcessing": null, | |
1255 | + "postFuncBody": null | |
1256 | + }, | |
1257 | + { | |
1258 | + "name": "sw_state", | |
1259 | + "type": "timeseries", | |
1260 | + "label": "Progress", | |
1261 | + "color": "#9c27b0", | |
1262 | + "settings": { | |
1263 | + "columnWidth": "30%", | |
1264 | + "useCellStyleFunction": true, | |
1265 | + "useCellContentFunction": true, | |
1266 | + "defaultColumnVisibility": "visible", | |
1267 | + "columnSelectionToDisplay": "enabled", | |
1268 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1269 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
1270 | + }, | |
1271 | + "_hash": 0.8174211757846257, | |
1272 | + "units": null, | |
1273 | + "decimals": null, | |
1274 | + "funcBody": null, | |
1275 | + "usePostProcessing": null, | |
1276 | + "postFuncBody": null | |
1277 | + }, | |
1278 | + { | |
1279 | + "name": "sw_state", | |
1280 | + "type": "timeseries", | |
1281 | + "label": "Status", | |
1282 | + "color": "#f44336", | |
1283 | + "settings": { | |
1284 | + "columnWidth": "130px", | |
1285 | + "useCellStyleFunction": true, | |
1286 | + "useCellContentFunction": true, | |
1287 | + "defaultColumnVisibility": "visible", | |
1288 | + "columnSelectionToDisplay": "enabled", | |
1289 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1290 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
1291 | + }, | |
1292 | + "_hash": 0.7764426948615217, | |
1293 | + "units": null, | |
1294 | + "decimals": null, | |
1295 | + "funcBody": null, | |
1296 | + "usePostProcessing": null, | |
1297 | + "postFuncBody": null | |
1298 | + }, | |
1299 | + { | |
1300 | + "name": "sw_checksum", | |
1301 | + "type": "attribute", | |
1302 | + "label": "sw_checksum", | |
1303 | + "color": "#3f51b5", | |
1304 | + "settings": { | |
1305 | + "columnWidth": "0px", | |
1306 | + "useCellStyleFunction": false, | |
1307 | + "cellStyleFunction": "", | |
1308 | + "useCellContentFunction": false, | |
1309 | + "defaultColumnVisibility": "hidden", | |
1310 | + "columnSelectionToDisplay": "disabled" | |
1311 | + }, | |
1312 | + "_hash": 0.5594087842471693, | |
1313 | + "units": null, | |
1314 | + "decimals": null, | |
1315 | + "funcBody": null, | |
1316 | + "usePostProcessing": null, | |
1317 | + "postFuncBody": null | |
1318 | + }, | |
1319 | + { | |
1320 | + "name": "sw_url", | |
1321 | + "type": "attribute", | |
1322 | + "label": "sw_url", | |
1323 | + "color": "#e91e63", | |
1324 | + "settings": { | |
1325 | + "columnWidth": "0px", | |
1326 | + "useCellStyleFunction": false, | |
1327 | + "cellStyleFunction": "", | |
1328 | + "useCellContentFunction": false, | |
1329 | + "cellContentFunction": "", | |
1330 | + "defaultColumnVisibility": "hidden", | |
1331 | + "columnSelectionToDisplay": "disabled" | |
1332 | + }, | |
1333 | + "_hash": 0.3355829384124256, | |
1334 | + "units": null, | |
1335 | + "decimals": null, | |
1336 | + "funcBody": null, | |
1337 | + "usePostProcessing": null, | |
1338 | + "postFuncBody": null | |
1339 | + } | |
1340 | + ] | |
1341 | + } | |
1342 | + ], | |
1343 | + "actions": { | |
1344 | + "actionCellButton": [ | |
1345 | + { | |
1346 | + "name": "History software update", | |
1347 | + "icon": "history", | |
1348 | + "type": "openDashboardState", | |
1349 | + "targetDashboardStateId": "device_software_history", | |
1350 | + "setEntityId": true, | |
1351 | + "stateEntityParamName": null, | |
1352 | + "openInSeparateDialog": false, | |
1353 | + "dialogTitle": "", | |
1354 | + "dialogHideDashboardToolbar": true, | |
1355 | + "dialogWidth": null, | |
1356 | + "dialogHeight": null, | |
1357 | + "openRightLayout": false, | |
1358 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1359 | + }, | |
1360 | + { | |
1361 | + "name": "Edit software", | |
1362 | + "icon": "edit", | |
1363 | + "type": "customPretty", | |
1364 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1365 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1366 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1367 | + "customResources": [], | |
1368 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1369 | + }, | |
1370 | + { | |
1371 | + "name": "Download software", | |
1372 | + "icon": "file_download", | |
1373 | + "type": "custom", | |
1374 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1375 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1376 | + }, | |
1377 | + { | |
1378 | + "name": "Copy checksum/URL", | |
1379 | + "icon": "content_copy", | |
1380 | + "type": "custom", | |
1381 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1382 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1383 | + } | |
1384 | + ] | |
1385 | + }, | |
1386 | + "showTitleIcon": false, | |
1387 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1388 | + "iconSize": "24px", | |
1389 | + "titleTooltip": "", | |
1390 | + "widgetStyle": {} | |
1391 | + }, | |
1392 | + "row": 0, | |
1393 | + "col": 0, | |
1394 | + "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91" | |
1395 | + }, | |
1396 | + "3624013b-378c-f110-5eba-ae95c25a4dcc": { | |
1397 | + "isSystemType": true, | |
1398 | + "bundleAlias": "cards", | |
1399 | + "typeAlias": "entities_table", | |
1400 | + "type": "latest", | |
1401 | + "title": "New widget", | |
1402 | + "image": null, | |
1403 | + "description": null, | |
1404 | + "sizeX": 7.5, | |
1405 | + "sizeY": 6.5, | |
1406 | + "config": { | |
1407 | + "timewindow": { | |
1408 | + "realtime": { | |
1409 | + "interval": 1000, | |
1410 | + "timewindowMs": 86400000 | |
1411 | + }, | |
1412 | + "aggregation": { | |
1413 | + "type": "NONE", | |
1414 | + "limit": 200 | |
1415 | + } | |
1416 | + }, | |
1417 | + "showTitle": true, | |
1418 | + "backgroundColor": "rgb(255, 255, 255)", | |
1419 | + "color": "rgba(0, 0, 0, 0.87)", | |
1420 | + "padding": "4px", | |
1421 | + "settings": { | |
1422 | + "enableSearch": true, | |
1423 | + "displayPagination": true, | |
1424 | + "defaultPageSize": 10, | |
1425 | + "defaultSortOrder": "entityLabel", | |
1426 | + "displayEntityName": false, | |
1427 | + "displayEntityType": false, | |
1428 | + "enableSelectColumnDisplay": false, | |
1429 | + "enableStickyHeader": true, | |
1430 | + "enableStickyAction": true, | |
1431 | + "entitiesTitle": "Devices", | |
1432 | + "displayEntityLabel": true, | |
1433 | + "entityLabelColumnTitle": "Device" | |
1434 | + }, | |
1435 | + "title": "New Entities table", | |
1436 | + "dropShadow": true, | |
1437 | + "enableFullscreen": true, | |
1438 | + "titleStyle": { | |
1439 | + "fontSize": "16px", | |
1440 | + "fontWeight": 400, | |
1441 | + "padding": "5px 10px 5px 10px" | |
1442 | + }, | |
1443 | + "useDashboardTimewindow": false, | |
1444 | + "showLegend": false, | |
1445 | + "datasources": [ | |
1446 | + { | |
1447 | + "type": "entity", | |
1448 | + "name": null, | |
1449 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1450 | + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
1451 | + "dataKeys": [ | |
1452 | + { | |
1453 | + "name": "current_sw_title", | |
1454 | + "type": "timeseries", | |
1455 | + "label": "Current SW title", | |
1456 | + "color": "#2196f3", | |
1457 | + "settings": { | |
1458 | + "columnWidth": "0px", | |
1459 | + "useCellStyleFunction": false, | |
1460 | + "cellStyleFunction": "", | |
1461 | + "useCellContentFunction": false, | |
1462 | + "defaultColumnVisibility": "visible", | |
1463 | + "columnSelectionToDisplay": "enabled" | |
1464 | + }, | |
1465 | + "_hash": 0.09545533885166413, | |
1466 | + "units": null, | |
1467 | + "decimals": null, | |
1468 | + "funcBody": null, | |
1469 | + "usePostProcessing": null, | |
1470 | + "postFuncBody": null | |
1471 | + }, | |
1472 | + { | |
1473 | + "name": "current_sw_version", | |
1474 | + "type": "timeseries", | |
1475 | + "label": "Current SW version", | |
1476 | + "color": "#4caf50", | |
1477 | + "settings": { | |
1478 | + "columnWidth": "0px", | |
1479 | + "useCellStyleFunction": false, | |
1480 | + "cellStyleFunction": "", | |
1481 | + "useCellContentFunction": false, | |
1482 | + "defaultColumnVisibility": "visible", | |
1483 | + "columnSelectionToDisplay": "enabled" | |
1484 | + }, | |
1485 | + "_hash": 0.7206056602328659, | |
1486 | + "units": null, | |
1487 | + "decimals": null, | |
1488 | + "funcBody": null, | |
1489 | + "usePostProcessing": null, | |
1490 | + "postFuncBody": null | |
1491 | + }, | |
1492 | + { | |
1493 | + "name": "target_sw_title", | |
1494 | + "type": "timeseries", | |
1495 | + "label": "Target SW title", | |
1496 | + "color": "#ffc107", | |
1497 | + "settings": { | |
1498 | + "columnWidth": "0px", | |
1499 | + "useCellStyleFunction": false, | |
1500 | + "cellStyleFunction": "", | |
1501 | + "useCellContentFunction": false, | |
1502 | + "defaultColumnVisibility": "visible", | |
1503 | + "columnSelectionToDisplay": "enabled" | |
1504 | + }, | |
1505 | + "_hash": 0.9934225682766313, | |
1506 | + "units": null, | |
1507 | + "decimals": null, | |
1508 | + "funcBody": null, | |
1509 | + "usePostProcessing": null, | |
1510 | + "postFuncBody": null | |
1511 | + }, | |
1512 | + { | |
1513 | + "name": "target_sw_version", | |
1514 | + "type": "timeseries", | |
1515 | + "label": "Target SW version", | |
1516 | + "color": "#607d8b", | |
1517 | + "settings": { | |
1518 | + "columnWidth": "0px", | |
1519 | + "useCellStyleFunction": false, | |
1520 | + "cellStyleFunction": "", | |
1521 | + "useCellContentFunction": false, | |
1522 | + "cellContentFunction": "", | |
1523 | + "defaultColumnVisibility": "visible", | |
1524 | + "columnSelectionToDisplay": "enabled" | |
1525 | + }, | |
1526 | + "_hash": 0.5251724416842531, | |
1527 | + "units": null, | |
1528 | + "decimals": null, | |
1529 | + "funcBody": null, | |
1530 | + "usePostProcessing": null, | |
1531 | + "postFuncBody": null | |
1532 | + }, | |
1533 | + { | |
1534 | + "name": "target_sw_ts", | |
1535 | + "type": "timeseries", | |
1536 | + "label": "Target SW set time", | |
1537 | + "color": "#e91e63", | |
1538 | + "settings": { | |
1539 | + "columnWidth": "0px", | |
1540 | + "useCellStyleFunction": false, | |
1541 | + "cellStyleFunction": "", | |
1542 | + "useCellContentFunction": true, | |
1543 | + "defaultColumnVisibility": "visible", | |
1544 | + "columnSelectionToDisplay": "enabled", | |
1545 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1546 | + }, | |
1547 | + "_hash": 0.31823244858578237, | |
1548 | + "units": null, | |
1549 | + "decimals": null, | |
1550 | + "funcBody": null, | |
1551 | + "usePostProcessing": null, | |
1552 | + "postFuncBody": null | |
1553 | + }, | |
1554 | + { | |
1555 | + "name": "sw_state", | |
1556 | + "type": "timeseries", | |
1557 | + "label": "Progress", | |
1558 | + "color": "#9c27b0", | |
1559 | + "settings": { | |
1560 | + "columnWidth": "30%", | |
1561 | + "useCellStyleFunction": true, | |
1562 | + "useCellContentFunction": true, | |
1563 | + "defaultColumnVisibility": "visible", | |
1564 | + "columnSelectionToDisplay": "enabled", | |
1565 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1566 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
1567 | + }, | |
1568 | + "_hash": 0.8174211757846257, | |
1569 | + "units": null, | |
1570 | + "decimals": null, | |
1571 | + "funcBody": null, | |
1572 | + "usePostProcessing": null, | |
1573 | + "postFuncBody": null | |
1574 | + }, | |
1575 | + { | |
1576 | + "name": "sw_state", | |
1577 | + "type": "timeseries", | |
1578 | + "label": "Status", | |
1579 | + "color": "#f44336", | |
1580 | + "settings": { | |
1581 | + "columnWidth": "130px", | |
1582 | + "useCellStyleFunction": true, | |
1583 | + "useCellContentFunction": true, | |
1584 | + "defaultColumnVisibility": "visible", | |
1585 | + "columnSelectionToDisplay": "enabled", | |
1586 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1587 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
1588 | + }, | |
1589 | + "_hash": 0.7764426948615217, | |
1590 | + "units": null, | |
1591 | + "decimals": null, | |
1592 | + "funcBody": null, | |
1593 | + "usePostProcessing": null, | |
1594 | + "postFuncBody": null | |
1595 | + }, | |
1596 | + { | |
1597 | + "name": "sw_checksum", | |
1598 | + "type": "attribute", | |
1599 | + "label": "sw_checksum", | |
1600 | + "color": "#3f51b5", | |
1601 | + "settings": { | |
1602 | + "columnWidth": "0px", | |
1603 | + "useCellStyleFunction": false, | |
1604 | + "cellStyleFunction": "", | |
1605 | + "useCellContentFunction": false, | |
1606 | + "defaultColumnVisibility": "hidden", | |
1607 | + "columnSelectionToDisplay": "disabled" | |
1608 | + }, | |
1609 | + "_hash": 0.5594087842471693, | |
1610 | + "units": null, | |
1611 | + "decimals": null, | |
1612 | + "funcBody": null, | |
1613 | + "usePostProcessing": null, | |
1614 | + "postFuncBody": null | |
1615 | + }, | |
1616 | + { | |
1617 | + "name": "sw_url", | |
1618 | + "type": "attribute", | |
1619 | + "label": "sw_url", | |
1620 | + "color": "#e91e63", | |
1621 | + "settings": { | |
1622 | + "columnWidth": "0px", | |
1623 | + "useCellStyleFunction": false, | |
1624 | + "cellStyleFunction": "", | |
1625 | + "useCellContentFunction": false, | |
1626 | + "cellContentFunction": "", | |
1627 | + "defaultColumnVisibility": "hidden", | |
1628 | + "columnSelectionToDisplay": "disabled" | |
1629 | + }, | |
1630 | + "_hash": 0.3355829384124256, | |
1631 | + "units": null, | |
1632 | + "decimals": null, | |
1633 | + "funcBody": null, | |
1634 | + "usePostProcessing": null, | |
1635 | + "postFuncBody": null | |
1636 | + } | |
1637 | + ] | |
1638 | + } | |
1639 | + ], | |
1640 | + "actions": { | |
1641 | + "actionCellButton": [ | |
1642 | + { | |
1643 | + "name": "History software update", | |
1644 | + "icon": "history", | |
1645 | + "type": "openDashboardState", | |
1646 | + "targetDashboardStateId": "device_software_history", | |
1647 | + "setEntityId": true, | |
1648 | + "stateEntityParamName": null, | |
1649 | + "openInSeparateDialog": false, | |
1650 | + "dialogTitle": "", | |
1651 | + "dialogHideDashboardToolbar": true, | |
1652 | + "dialogWidth": null, | |
1653 | + "dialogHeight": null, | |
1654 | + "openRightLayout": false, | |
1655 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1656 | + }, | |
1657 | + { | |
1658 | + "name": "Edit software", | |
1659 | + "icon": "edit", | |
1660 | + "type": "customPretty", | |
1661 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1662 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1663 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1664 | + "customResources": [], | |
1665 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1666 | + }, | |
1667 | + { | |
1668 | + "name": "Download software", | |
1669 | + "icon": "file_download", | |
1670 | + "type": "custom", | |
1671 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1672 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1673 | + }, | |
1674 | + { | |
1675 | + "name": "Copy checksum/URL", | |
1676 | + "icon": "content_copy", | |
1677 | + "type": "custom", | |
1678 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1679 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1680 | + } | |
1681 | + ] | |
1682 | + }, | |
1683 | + "showTitleIcon": false, | |
1684 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1685 | + "iconSize": "24px", | |
1686 | + "titleTooltip": "", | |
1687 | + "widgetStyle": {} | |
1688 | + }, | |
1689 | + "row": 0, | |
1690 | + "col": 0, | |
1691 | + "id": "3624013b-378c-f110-5eba-ae95c25a4dcc" | |
1692 | + }, | |
1693 | + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { | |
1694 | + "isSystemType": true, | |
1695 | + "bundleAlias": "cards", | |
1696 | + "typeAlias": "entities_table", | |
1697 | + "type": "latest", | |
1698 | + "title": "New widget", | |
1699 | + "image": null, | |
1700 | + "description": null, | |
1701 | + "sizeX": 7.5, | |
1702 | + "sizeY": 6.5, | |
1703 | + "config": { | |
1704 | + "timewindow": { | |
1705 | + "realtime": { | |
1706 | + "interval": 1000, | |
1707 | + "timewindowMs": 86400000 | |
1708 | + }, | |
1709 | + "aggregation": { | |
1710 | + "type": "NONE", | |
1711 | + "limit": 200 | |
1712 | + } | |
1713 | + }, | |
1714 | + "showTitle": true, | |
1715 | + "backgroundColor": "rgb(255, 255, 255)", | |
1716 | + "color": "rgba(0, 0, 0, 0.87)", | |
1717 | + "padding": "4px", | |
1718 | + "settings": { | |
1719 | + "enableSearch": true, | |
1720 | + "displayPagination": true, | |
1721 | + "defaultPageSize": 10, | |
1722 | + "defaultSortOrder": "entityLabel", | |
1723 | + "displayEntityName": false, | |
1724 | + "displayEntityType": false, | |
1725 | + "enableSelectColumnDisplay": false, | |
1726 | + "enableStickyHeader": true, | |
1727 | + "enableStickyAction": true, | |
1728 | + "entitiesTitle": "Devices", | |
1729 | + "displayEntityLabel": true, | |
1730 | + "entityLabelColumnTitle": "Device" | |
1731 | + }, | |
1732 | + "title": "New Entities table", | |
1733 | + "dropShadow": true, | |
1734 | + "enableFullscreen": true, | |
1735 | + "titleStyle": { | |
1736 | + "fontSize": "16px", | |
1737 | + "fontWeight": 400, | |
1738 | + "padding": "5px 10px 5px 10px" | |
1739 | + }, | |
1740 | + "useDashboardTimewindow": false, | |
1741 | + "showLegend": false, | |
1742 | + "datasources": [ | |
1743 | + { | |
1744 | + "type": "entity", | |
1745 | + "name": null, | |
1746 | + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
1747 | + "filterId": "6044e198-df64-cd76-f339-696f220c4943", | |
1748 | + "dataKeys": [ | |
1749 | + { | |
1750 | + "name": "current_sw_title", | |
1751 | + "type": "timeseries", | |
1752 | + "label": "Current SW title", | |
1753 | + "color": "#2196f3", | |
1754 | + "settings": { | |
1755 | + "columnWidth": "0px", | |
1756 | + "useCellStyleFunction": false, | |
1757 | + "cellStyleFunction": "", | |
1758 | + "useCellContentFunction": false, | |
1759 | + "defaultColumnVisibility": "visible", | |
1760 | + "columnSelectionToDisplay": "enabled" | |
1761 | + }, | |
1762 | + "_hash": 0.09545533885166413, | |
1763 | + "units": null, | |
1764 | + "decimals": null, | |
1765 | + "funcBody": null, | |
1766 | + "usePostProcessing": null, | |
1767 | + "postFuncBody": null | |
1768 | + }, | |
1769 | + { | |
1770 | + "name": "current_sw_version", | |
1771 | + "type": "timeseries", | |
1772 | + "label": "Current SW version", | |
1773 | + "color": "#4caf50", | |
1774 | + "settings": { | |
1775 | + "columnWidth": "0px", | |
1776 | + "useCellStyleFunction": false, | |
1777 | + "cellStyleFunction": "", | |
1778 | + "useCellContentFunction": false, | |
1779 | + "defaultColumnVisibility": "visible", | |
1780 | + "columnSelectionToDisplay": "enabled" | |
1781 | + }, | |
1782 | + "_hash": 0.7206056602328659, | |
1783 | + "units": null, | |
1784 | + "decimals": null, | |
1785 | + "funcBody": null, | |
1786 | + "usePostProcessing": null, | |
1787 | + "postFuncBody": null | |
1788 | + }, | |
1789 | + { | |
1790 | + "name": "target_sw_title", | |
1791 | + "type": "timeseries", | |
1792 | + "label": "Target SW title", | |
1793 | + "color": "#ffc107", | |
1794 | + "settings": { | |
1795 | + "columnWidth": "0px", | |
1796 | + "useCellStyleFunction": false, | |
1797 | + "cellStyleFunction": "", | |
1798 | + "useCellContentFunction": false, | |
1799 | + "defaultColumnVisibility": "visible", | |
1800 | + "columnSelectionToDisplay": "enabled" | |
1801 | + }, | |
1802 | + "_hash": 0.9934225682766313, | |
1803 | + "units": null, | |
1804 | + "decimals": null, | |
1805 | + "funcBody": null, | |
1806 | + "usePostProcessing": null, | |
1807 | + "postFuncBody": null | |
1808 | + }, | |
1809 | + { | |
1810 | + "name": "target_sw_version", | |
1811 | + "type": "timeseries", | |
1812 | + "label": "Target SW version", | |
1813 | + "color": "#607d8b", | |
1814 | + "settings": { | |
1815 | + "columnWidth": "0px", | |
1816 | + "useCellStyleFunction": false, | |
1817 | + "cellStyleFunction": "", | |
1818 | + "useCellContentFunction": false, | |
1819 | + "cellContentFunction": "", | |
1820 | + "defaultColumnVisibility": "visible", | |
1821 | + "columnSelectionToDisplay": "enabled" | |
1822 | + }, | |
1823 | + "_hash": 0.5251724416842531, | |
1824 | + "units": null, | |
1825 | + "decimals": null, | |
1826 | + "funcBody": null, | |
1827 | + "usePostProcessing": null, | |
1828 | + "postFuncBody": null | |
1829 | + }, | |
1830 | + { | |
1831 | + "name": "target_sw_ts", | |
1832 | + "type": "timeseries", | |
1833 | + "label": "Target SW set time", | |
1834 | + "color": "#e91e63", | |
1835 | + "settings": { | |
1836 | + "columnWidth": "0px", | |
1837 | + "useCellStyleFunction": false, | |
1838 | + "cellStyleFunction": "", | |
1839 | + "useCellContentFunction": true, | |
1840 | + "defaultColumnVisibility": "visible", | |
1841 | + "columnSelectionToDisplay": "enabled", | |
1842 | + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" | |
1843 | + }, | |
1844 | + "_hash": 0.31823244858578237, | |
1845 | + "units": null, | |
1846 | + "decimals": null, | |
1847 | + "funcBody": null, | |
1848 | + "usePostProcessing": null, | |
1849 | + "postFuncBody": null | |
1850 | + }, | |
1851 | + { | |
1852 | + "name": "sw_state", | |
1853 | + "type": "timeseries", | |
1854 | + "label": "Progress", | |
1855 | + "color": "#9c27b0", | |
1856 | + "settings": { | |
1857 | + "columnWidth": "30%", | |
1858 | + "useCellStyleFunction": true, | |
1859 | + "useCellContentFunction": true, | |
1860 | + "defaultColumnVisibility": "visible", | |
1861 | + "columnSelectionToDisplay": "enabled", | |
1862 | + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", | |
1863 | + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `<mat-progress-bar style=\"height: 8px\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" tabindex=\"-1\" mode=\"determinate\" value=\"${progress}\" class=\"mat-progress-bar ${color}\" aria-valuenow=\"${progress}\"><div aria-hidden=\"true\"><svg width=\"100%\" height=\"8\" focusable=\"false\" class=\"mat-progress-bar-background mat-progress-bar-element\"><defs><pattern x=\"4\" y=\"0\" width=\"8\" height=\"4\" patternUnits=\"userSpaceOnUse\" id=\"mat-progress-bar-0\"><circle cx=\"2\" cy=\"2\" r=\"2\"></circle></pattern></defs><rect width=\"100%\" height=\"100%\" fill=\"url(\"/components/progress-bar/overview#mat-progress-bar-0\")\"></rect></svg><div class=\"mat-progress-bar-buffer mat-progress-bar-element\"></div><div class=\"mat-progress-bar-primary mat-progress-bar-fill mat-progress-bar-element\" style=\"transform: scale3d(${progress / 100}, 1, 1);\"></div><div class=\"mat-progress-bar-secondary mat-progress-bar-fill mat-progress-bar-element\"></div></div></mat-progress-bar>`;\n}" | |
1864 | + }, | |
1865 | + "_hash": 0.8174211757846257, | |
1866 | + "units": null, | |
1867 | + "decimals": null, | |
1868 | + "funcBody": null, | |
1869 | + "usePostProcessing": null, | |
1870 | + "postFuncBody": null | |
1871 | + }, | |
1872 | + { | |
1873 | + "name": "sw_state", | |
1874 | + "type": "timeseries", | |
1875 | + "label": "Status", | |
1876 | + "color": "#f44336", | |
1877 | + "settings": { | |
1878 | + "columnWidth": "130px", | |
1879 | + "useCellStyleFunction": true, | |
1880 | + "useCellContentFunction": true, | |
1881 | + "defaultColumnVisibility": "visible", | |
1882 | + "columnSelectionToDisplay": "enabled", | |
1883 | + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", | |
1884 | + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000;\"><svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6,2V8H6V8L10,12L6,16V16H6V22H18V16H18V16L14,12L18,8V8H18V2H6M16,16.5V20H8V16.5L12,12.5L16,16.5M12,11.5L8,7.5V4H16V7.5L12,11.5Z\" /></svg></mat-icon>';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.74 2.1951C11.63 1.2876 10.2575 0.687598 8.75 0.537598V2.0526C9.845 2.1876 10.8425 2.6226 11.675 3.2676L12.74 2.1951ZM13.9475 7.2501H15.4625C15.3125 5.7426 14.7125 4.3701 13.805 3.2601L12.7325 4.3251C13.3775 5.1576 13.8125 6.1551 13.9475 7.2501ZM12.7325 11.6751L13.805 12.7476C14.7125 11.6376 15.3125 10.2576 15.4625 8.7576H13.9475C13.8125 9.8451 13.3775 10.8426 12.7325 11.6751ZM8.75 13.9476V15.4626C10.2575 15.3126 11.63 14.7126 12.74 13.8051L11.6675 12.7326C10.8425 13.3776 9.845 13.8126 8.75 13.9476ZM8.75 8.0001V4.2501H7.25V8.0001H4.25L8 11.7501L11.75 8.0001H8.75ZM7.25 13.9476V15.4626C3.4625 15.0876 0.5 11.8926 0.5 8.0001C0.5 4.1076 3.4625 0.912598 7.25 0.537598V2.0526C4.2875 2.4201 2 4.9401 2 8.0001C2 11.0601 4.2875 13.5801 7.25 13.9476Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\">update</mat-icon>';\n }\n if (value == 'UPDATED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #000\"><svg style=\"width:22px;height:22px\" viewBox=\"0 0 34 32\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M33.26 2.82L30.44 0L12.06 18.38L3.55999 9.9L0.73999 12.72L12.06 24.04L33.26 2.82Z\" fill=\"black\"/><path d=\"M31 28H3V32H31V28Z\" fill=\"black\"/></svg></mat-icon>';\n }\n if (value == 'FAILED') {\n return '<mat-icon _role=\"img\" class=\"mat-icon notranslate material-icons mat-icon-no-color\" aria-hidden=\"true\" data-mat-icon-type=\"font\" style=\"color: #D93025\">warning</mat-icon>';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '<span style=\"vertical-align: super;padding-left: 8px;\">' + capitalize(value) + '</span>';" | |
1885 | + }, | |
1886 | + "_hash": 0.7764426948615217, | |
1887 | + "units": null, | |
1888 | + "decimals": null, | |
1889 | + "funcBody": null, | |
1890 | + "usePostProcessing": null, | |
1891 | + "postFuncBody": null | |
1892 | + }, | |
1893 | + { | |
1894 | + "name": "sw_checksum", | |
1895 | + "type": "attribute", | |
1896 | + "label": "sw_checksum", | |
1897 | + "color": "#3f51b5", | |
1898 | + "settings": { | |
1899 | + "columnWidth": "0px", | |
1900 | + "useCellStyleFunction": false, | |
1901 | + "cellStyleFunction": "", | |
1902 | + "useCellContentFunction": false, | |
1903 | + "defaultColumnVisibility": "hidden", | |
1904 | + "columnSelectionToDisplay": "disabled" | |
1905 | + }, | |
1906 | + "_hash": 0.5594087842471693, | |
1907 | + "units": null, | |
1908 | + "decimals": null, | |
1909 | + "funcBody": null, | |
1910 | + "usePostProcessing": null, | |
1911 | + "postFuncBody": null | |
1912 | + }, | |
1913 | + { | |
1914 | + "name": "sw_url", | |
1915 | + "type": "attribute", | |
1916 | + "label": "sw_url", | |
1917 | + "color": "#e91e63", | |
1918 | + "settings": { | |
1919 | + "columnWidth": "0px", | |
1920 | + "useCellStyleFunction": false, | |
1921 | + "cellStyleFunction": "", | |
1922 | + "useCellContentFunction": false, | |
1923 | + "cellContentFunction": "", | |
1924 | + "defaultColumnVisibility": "hidden", | |
1925 | + "columnSelectionToDisplay": "disabled" | |
1926 | + }, | |
1927 | + "_hash": 0.3355829384124256, | |
1928 | + "units": null, | |
1929 | + "decimals": null, | |
1930 | + "funcBody": null, | |
1931 | + "usePostProcessing": null, | |
1932 | + "postFuncBody": null | |
1933 | + } | |
1934 | + ] | |
1935 | + } | |
1936 | + ], | |
1937 | + "actions": { | |
1938 | + "actionCellButton": [ | |
1939 | + { | |
1940 | + "name": "History software update", | |
1941 | + "icon": "history", | |
1942 | + "type": "openDashboardState", | |
1943 | + "targetDashboardStateId": "device_software_history", | |
1944 | + "setEntityId": true, | |
1945 | + "stateEntityParamName": null, | |
1946 | + "openInSeparateDialog": false, | |
1947 | + "dialogTitle": "", | |
1948 | + "dialogHideDashboardToolbar": true, | |
1949 | + "dialogWidth": null, | |
1950 | + "dialogHeight": null, | |
1951 | + "openRightLayout": false, | |
1952 | + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" | |
1953 | + }, | |
1954 | + { | |
1955 | + "name": "Edit software", | |
1956 | + "icon": "edit", | |
1957 | + "type": "customPretty", | |
1958 | + "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit software {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n type=\"SOFTWARE\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"softwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>", | |
1959 | + "customCss": "form {\n min-width: 300px !important;\n}", | |
1960 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", | |
1961 | + "customResources": [], | |
1962 | + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" | |
1963 | + }, | |
1964 | + { | |
1965 | + "name": "Download software", | |
1966 | + "icon": "file_download", | |
1967 | + "type": "custom", | |
1968 | + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1969 | + "id": "12533058-42f6-e75f-620c-219c48d01ec0" | |
1970 | + }, | |
1971 | + { | |
1972 | + "name": "Copy checksum/URL", | |
1973 | + "icon": "content_copy", | |
1974 | + "type": "custom", | |
1975 | + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", | |
1976 | + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" | |
1977 | + } | |
1978 | + ] | |
1979 | + }, | |
1980 | + "showTitleIcon": false, | |
1981 | + "iconColor": "rgba(0, 0, 0, 0.87)", | |
1982 | + "iconSize": "24px", | |
1983 | + "titleTooltip": "", | |
1984 | + "widgetStyle": {} | |
1985 | + }, | |
1986 | + "row": 0, | |
1987 | + "col": 0, | |
1988 | + "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176" | |
1989 | + } | |
1990 | + }, | |
1991 | + "states": { | |
1992 | + "default": { | |
1993 | + "name": "Device list", | |
1994 | + "root": true, | |
1995 | + "layouts": { | |
1996 | + "main": { | |
1997 | + "widgets": { | |
1998 | + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { | |
1999 | + "sizeX": 19, | |
2000 | + "sizeY": 12, | |
2001 | + "row": 0, | |
2002 | + "col": 0 | |
2003 | + }, | |
2004 | + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { | |
2005 | + "sizeX": 5, | |
2006 | + "sizeY": 3, | |
2007 | + "row": 0, | |
2008 | + "col": 19 | |
2009 | + }, | |
2010 | + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { | |
2011 | + "sizeX": 5, | |
2012 | + "sizeY": 3, | |
2013 | + "row": 3, | |
2014 | + "col": 19 | |
2015 | + }, | |
2016 | + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { | |
2017 | + "sizeX": 5, | |
2018 | + "sizeY": 3, | |
2019 | + "row": 9, | |
2020 | + "col": 19 | |
2021 | + }, | |
2022 | + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { | |
2023 | + "sizeX": 5, | |
2024 | + "sizeY": 3, | |
2025 | + "row": 6, | |
2026 | + "col": 19 | |
2027 | + } | |
2028 | + }, | |
2029 | + "gridSettings": { | |
2030 | + "backgroundColor": "#eeeeee", | |
2031 | + "color": "rgba(0,0,0,0.870588)", | |
2032 | + "columns": 24, | |
2033 | + "margin": 12, | |
2034 | + "backgroundSizeMode": "100%", | |
2035 | + "autoFillHeight": true, | |
2036 | + "backgroundImageUrl": null, | |
2037 | + "mobileAutoFillHeight": true, | |
2038 | + "mobileRowHeight": 70 | |
2039 | + } | |
2040 | + } | |
2041 | + } | |
2042 | + }, | |
2043 | + "device_software_history": { | |
2044 | + "name": "Software history: ${entityName}", | |
2045 | + "root": false, | |
2046 | + "layouts": { | |
2047 | + "main": { | |
2048 | + "widgets": { | |
2049 | + "100b756c-0082-6505-3ae1-3603e6deea48": { | |
2050 | + "sizeX": 24, | |
2051 | + "sizeY": 12, | |
2052 | + "row": 0, | |
2053 | + "col": 0 | |
2054 | + } | |
2055 | + }, | |
2056 | + "gridSettings": { | |
2057 | + "backgroundColor": "#eeeeee", | |
2058 | + "color": "rgba(0,0,0,0.870588)", | |
2059 | + "columns": 24, | |
2060 | + "margin": 10, | |
2061 | + "backgroundSizeMode": "100%", | |
2062 | + "autoFillHeight": true, | |
2063 | + "backgroundImageUrl": null, | |
2064 | + "mobileAutoFillHeight": false, | |
2065 | + "mobileRowHeight": 70 | |
2066 | + } | |
2067 | + } | |
2068 | + } | |
2069 | + }, | |
2070 | + "device_waiting": { | |
2071 | + "name": "Device waiting", | |
2072 | + "root": false, | |
2073 | + "layouts": { | |
2074 | + "main": { | |
2075 | + "widgets": { | |
2076 | + "21be08bb-ec90-f760-ad6f-e7678f12c401": { | |
2077 | + "sizeX": 24, | |
2078 | + "sizeY": 12, | |
2079 | + "row": 0, | |
2080 | + "col": 0 | |
2081 | + } | |
2082 | + }, | |
2083 | + "gridSettings": { | |
2084 | + "backgroundColor": "#eeeeee", | |
2085 | + "color": "rgba(0,0,0,0.870588)", | |
2086 | + "columns": 24, | |
2087 | + "margin": 10, | |
2088 | + "backgroundSizeMode": "100%", | |
2089 | + "autoFillHeight": true, | |
2090 | + "backgroundImageUrl": null, | |
2091 | + "mobileAutoFillHeight": false, | |
2092 | + "mobileRowHeight": 70 | |
2093 | + } | |
2094 | + } | |
2095 | + } | |
2096 | + }, | |
2097 | + "device_updating": { | |
2098 | + "name": "Device updating", | |
2099 | + "root": false, | |
2100 | + "layouts": { | |
2101 | + "main": { | |
2102 | + "widgets": { | |
2103 | + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { | |
2104 | + "sizeX": 24, | |
2105 | + "sizeY": 12, | |
2106 | + "row": 0, | |
2107 | + "col": 0 | |
2108 | + } | |
2109 | + }, | |
2110 | + "gridSettings": { | |
2111 | + "backgroundColor": "#eeeeee", | |
2112 | + "color": "rgba(0,0,0,0.870588)", | |
2113 | + "columns": 24, | |
2114 | + "margin": 10, | |
2115 | + "backgroundSizeMode": "100%", | |
2116 | + "autoFillHeight": true, | |
2117 | + "backgroundImageUrl": null, | |
2118 | + "mobileAutoFillHeight": false, | |
2119 | + "mobileRowHeight": 70 | |
2120 | + } | |
2121 | + } | |
2122 | + } | |
2123 | + }, | |
2124 | + "device_updated": { | |
2125 | + "name": "Device updated", | |
2126 | + "root": false, | |
2127 | + "layouts": { | |
2128 | + "main": { | |
2129 | + "widgets": { | |
2130 | + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { | |
2131 | + "sizeX": 27, | |
2132 | + "sizeY": 12, | |
2133 | + "row": 0, | |
2134 | + "col": 0 | |
2135 | + } | |
2136 | + }, | |
2137 | + "gridSettings": { | |
2138 | + "backgroundColor": "#eeeeee", | |
2139 | + "color": "rgba(0,0,0,0.870588)", | |
2140 | + "columns": 24, | |
2141 | + "margin": 10, | |
2142 | + "backgroundSizeMode": "100%", | |
2143 | + "autoFillHeight": true, | |
2144 | + "backgroundImageUrl": null, | |
2145 | + "mobileAutoFillHeight": false, | |
2146 | + "mobileRowHeight": 70 | |
2147 | + } | |
2148 | + } | |
2149 | + } | |
2150 | + }, | |
2151 | + "device_error": { | |
2152 | + "name": "Device failed", | |
2153 | + "root": false, | |
2154 | + "layouts": { | |
2155 | + "main": { | |
2156 | + "widgets": { | |
2157 | + "3624013b-378c-f110-5eba-ae95c25a4dcc": { | |
2158 | + "sizeX": 24, | |
2159 | + "sizeY": 12, | |
2160 | + "row": 0, | |
2161 | + "col": 0 | |
2162 | + } | |
2163 | + }, | |
2164 | + "gridSettings": { | |
2165 | + "backgroundColor": "#eeeeee", | |
2166 | + "color": "rgba(0,0,0,0.870588)", | |
2167 | + "columns": 24, | |
2168 | + "margin": 10, | |
2169 | + "backgroundSizeMode": "100%", | |
2170 | + "autoFillHeight": true, | |
2171 | + "backgroundImageUrl": null, | |
2172 | + "mobileAutoFillHeight": false, | |
2173 | + "mobileRowHeight": 70 | |
2174 | + } | |
2175 | + } | |
2176 | + } | |
2177 | + } | |
2178 | + }, | |
2179 | + "entityAliases": { | |
2180 | + "639da5b4-31f0-0151-6282-c37a3897b7e8": { | |
2181 | + "id": "639da5b4-31f0-0151-6282-c37a3897b7e8", | |
2182 | + "alias": "All devices", | |
2183 | + "filter": { | |
2184 | + "type": "entityType", | |
2185 | + "resolveMultiple": true, | |
2186 | + "entityType": "DEVICE" | |
2187 | + } | |
2188 | + }, | |
2189 | + "19f41c21-d9af-e666-8f50-e1748778f955": { | |
2190 | + "id": "19f41c21-d9af-e666-8f50-e1748778f955", | |
2191 | + "alias": "State entity", | |
2192 | + "filter": { | |
2193 | + "type": "stateEntity", | |
2194 | + "resolveMultiple": false, | |
2195 | + "stateEntityParamName": null, | |
2196 | + "defaultStateEntity": null | |
2197 | + } | |
2198 | + } | |
2199 | + }, | |
2200 | + "filters": { | |
2201 | + "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e": { | |
2202 | + "id": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", | |
2203 | + "filter": "WaitingDevicesFilter", | |
2204 | + "keyFilters": [ | |
2205 | + { | |
2206 | + "key": { | |
2207 | + "type": "TIME_SERIES", | |
2208 | + "key": "sw_state" | |
2209 | + }, | |
2210 | + "valueType": "STRING", | |
2211 | + "predicates": [ | |
2212 | + { | |
2213 | + "keyFilterPredicate": { | |
2214 | + "operation": "EQUAL", | |
2215 | + "value": { | |
2216 | + "defaultValue": "QUEUED", | |
2217 | + "dynamicValue": null | |
2218 | + }, | |
2219 | + "ignoreCase": false, | |
2220 | + "type": "STRING" | |
2221 | + }, | |
2222 | + "userInfo": { | |
2223 | + "editable": true, | |
2224 | + "label": "", | |
2225 | + "autogeneratedLabel": true, | |
2226 | + "order": 0 | |
2227 | + } | |
2228 | + } | |
2229 | + ] | |
2230 | + } | |
2231 | + ], | |
2232 | + "editable": false | |
2233 | + }, | |
2234 | + "579f0468-9ce9-7e3e-b34c-88dd3de59897": { | |
2235 | + "id": "579f0468-9ce9-7e3e-b34c-88dd3de59897", | |
2236 | + "filter": "UpdatingDevicesFilter", | |
2237 | + "keyFilters": [ | |
2238 | + { | |
2239 | + "key": { | |
2240 | + "type": "TIME_SERIES", | |
2241 | + "key": "sw_state" | |
2242 | + }, | |
2243 | + "valueType": "STRING", | |
2244 | + "predicates": [ | |
2245 | + { | |
2246 | + "keyFilterPredicate": { | |
2247 | + "operation": "OR", | |
2248 | + "predicates": [ | |
2249 | + { | |
2250 | + "keyFilterPredicate": { | |
2251 | + "operation": "EQUAL", | |
2252 | + "value": { | |
2253 | + "defaultValue": "INITIATED", | |
2254 | + "dynamicValue": null | |
2255 | + }, | |
2256 | + "ignoreCase": false, | |
2257 | + "type": "STRING" | |
2258 | + }, | |
2259 | + "userInfo": { | |
2260 | + "editable": false, | |
2261 | + "label": "sw_state equel", | |
2262 | + "autogeneratedLabel": true, | |
2263 | + "order": 0 | |
2264 | + } | |
2265 | + }, | |
2266 | + { | |
2267 | + "keyFilterPredicate": { | |
2268 | + "operation": "EQUAL", | |
2269 | + "value": { | |
2270 | + "defaultValue": "DOWNLOADING", | |
2271 | + "dynamicValue": null | |
2272 | + }, | |
2273 | + "ignoreCase": false, | |
2274 | + "type": "STRING" | |
2275 | + }, | |
2276 | + "userInfo": { | |
2277 | + "editable": false, | |
2278 | + "label": "sw_state equal", | |
2279 | + "autogeneratedLabel": true, | |
2280 | + "order": 0 | |
2281 | + } | |
2282 | + }, | |
2283 | + { | |
2284 | + "keyFilterPredicate": { | |
2285 | + "operation": "EQUAL", | |
2286 | + "value": { | |
2287 | + "defaultValue": "DOWNLOADED", | |
2288 | + "dynamicValue": null | |
2289 | + }, | |
2290 | + "ignoreCase": false, | |
2291 | + "type": "STRING" | |
2292 | + }, | |
2293 | + "userInfo": { | |
2294 | + "editable": false, | |
2295 | + "label": "sw_state equal", | |
2296 | + "autogeneratedLabel": true, | |
2297 | + "order": 0 | |
2298 | + } | |
2299 | + }, | |
2300 | + { | |
2301 | + "keyFilterPredicate": { | |
2302 | + "operation": "EQUAL", | |
2303 | + "value": { | |
2304 | + "defaultValue": "VERIFIED", | |
2305 | + "dynamicValue": null | |
2306 | + }, | |
2307 | + "ignoreCase": false, | |
2308 | + "type": "STRING" | |
2309 | + }, | |
2310 | + "userInfo": { | |
2311 | + "editable": false, | |
2312 | + "label": "sw_state equal", | |
2313 | + "autogeneratedLabel": true, | |
2314 | + "order": 0 | |
2315 | + } | |
2316 | + }, | |
2317 | + { | |
2318 | + "keyFilterPredicate": { | |
2319 | + "operation": "EQUAL", | |
2320 | + "value": { | |
2321 | + "defaultValue": "UPDATING", | |
2322 | + "dynamicValue": null | |
2323 | + }, | |
2324 | + "ignoreCase": false, | |
2325 | + "type": "STRING" | |
2326 | + }, | |
2327 | + "userInfo": { | |
2328 | + "editable": false, | |
2329 | + "label": "sw_state equal", | |
2330 | + "autogeneratedLabel": true, | |
2331 | + "order": 0 | |
2332 | + } | |
2333 | + } | |
2334 | + ], | |
2335 | + "type": "COMPLEX" | |
2336 | + }, | |
2337 | + "userInfo": { | |
2338 | + "editable": true, | |
2339 | + "label": "", | |
2340 | + "autogeneratedLabel": true, | |
2341 | + "order": 0 | |
2342 | + } | |
2343 | + } | |
2344 | + ] | |
2345 | + } | |
2346 | + ], | |
2347 | + "editable": false | |
2348 | + }, | |
2349 | + "6044e198-df64-cd76-f339-696f220c4943": { | |
2350 | + "id": "6044e198-df64-cd76-f339-696f220c4943", | |
2351 | + "filter": "UpdetedDevicesFilter", | |
2352 | + "keyFilters": [ | |
2353 | + { | |
2354 | + "key": { | |
2355 | + "type": "TIME_SERIES", | |
2356 | + "key": "sw_state" | |
2357 | + }, | |
2358 | + "valueType": "STRING", | |
2359 | + "predicates": [ | |
2360 | + { | |
2361 | + "keyFilterPredicate": { | |
2362 | + "operation": "EQUAL", | |
2363 | + "value": { | |
2364 | + "defaultValue": "UPDATED", | |
2365 | + "dynamicValue": null | |
2366 | + }, | |
2367 | + "ignoreCase": false, | |
2368 | + "type": "STRING" | |
2369 | + }, | |
2370 | + "userInfo": { | |
2371 | + "editable": true, | |
2372 | + "label": "", | |
2373 | + "autogeneratedLabel": true, | |
2374 | + "order": 0 | |
2375 | + } | |
2376 | + } | |
2377 | + ] | |
2378 | + } | |
2379 | + ], | |
2380 | + "editable": false | |
2381 | + }, | |
2382 | + "bdbc6ea1-95a7-3912-341a-58dc7704a00f": { | |
2383 | + "id": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", | |
2384 | + "filter": "FailedDevicesFilter", | |
2385 | + "keyFilters": [ | |
2386 | + { | |
2387 | + "key": { | |
2388 | + "type": "TIME_SERIES", | |
2389 | + "key": "sw_state" | |
2390 | + }, | |
2391 | + "valueType": "STRING", | |
2392 | + "predicates": [ | |
2393 | + { | |
2394 | + "keyFilterPredicate": { | |
2395 | + "operation": "EQUAL", | |
2396 | + "value": { | |
2397 | + "defaultValue": "FAILED", | |
2398 | + "dynamicValue": null | |
2399 | + }, | |
2400 | + "ignoreCase": false, | |
2401 | + "type": "STRING" | |
2402 | + }, | |
2403 | + "userInfo": { | |
2404 | + "editable": true, | |
2405 | + "label": "", | |
2406 | + "autogeneratedLabel": true, | |
2407 | + "order": 0 | |
2408 | + } | |
2409 | + } | |
2410 | + ] | |
2411 | + } | |
2412 | + ], | |
2413 | + "editable": false | |
2414 | + }, | |
2415 | + "8fdb88d0-50ac-2232-fdb7-69c30c16544e": { | |
2416 | + "id": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", | |
2417 | + "filter": "DeviceSearch", | |
2418 | + "keyFilters": [ | |
2419 | + { | |
2420 | + "key": { | |
2421 | + "type": "ENTITY_FIELD", | |
2422 | + "key": "name" | |
2423 | + }, | |
2424 | + "valueType": "STRING", | |
2425 | + "predicates": [ | |
2426 | + { | |
2427 | + "keyFilterPredicate": { | |
2428 | + "operation": "CONTAINS", | |
2429 | + "value": { | |
2430 | + "defaultValue": "" | |
2431 | + }, | |
2432 | + "ignoreCase": true, | |
2433 | + "type": "STRING" | |
2434 | + }, | |
2435 | + "userInfo": { | |
2436 | + "editable": true, | |
2437 | + "label": "Device name", | |
2438 | + "autogeneratedLabel": false, | |
2439 | + "order": 0 | |
2440 | + } | |
2441 | + } | |
2442 | + ] | |
2443 | + } | |
2444 | + ], | |
2445 | + "editable": true | |
2446 | + } | |
2447 | + }, | |
2448 | + "timewindow": { | |
2449 | + "displayValue": "", | |
2450 | + "hideInterval": false, | |
2451 | + "hideAggregation": false, | |
2452 | + "hideAggInterval": false, | |
2453 | + "hideTimezone": false, | |
2454 | + "selectedTab": 0, | |
2455 | + "realtime": { | |
2456 | + "realtimeType": 0, | |
2457 | + "interval": 1000, | |
2458 | + "timewindowMs": 60000, | |
2459 | + "quickInterval": "CURRENT_DAY" | |
2460 | + }, | |
2461 | + "history": { | |
2462 | + "historyType": 0, | |
2463 | + "interval": 1000, | |
2464 | + "timewindowMs": 60000, | |
2465 | + "fixedTimewindow": { | |
2466 | + "startTimeMs": 1618998609030, | |
2467 | + "endTimeMs": 1619085009030 | |
2468 | + }, | |
2469 | + "quickInterval": "CURRENT_DAY" | |
2470 | + }, | |
2471 | + "aggregation": { | |
2472 | + "type": "AVG", | |
2473 | + "limit": 25000 | |
2474 | + } | |
2475 | + }, | |
2476 | + "settings": { | |
2477 | + "stateControllerId": "entity", | |
2478 | + "showTitle": false, | |
2479 | + "showDashboardsSelect": false, | |
2480 | + "showEntitiesSelect": false, | |
2481 | + "showDashboardTimewindow": true, | |
2482 | + "showDashboardExport": false, | |
2483 | + "toolbarAlwaysOpen": true, | |
2484 | + "titleColor": "rgba(0,0,0,0.870588)", | |
2485 | + "showFilters": true, | |
2486 | + "showDashboardLogo": false, | |
2487 | + "dashboardLogoUrl": null, | |
2488 | + "showUpdateDashboardImage": false | |
2489 | + } | |
2490 | + }, | |
2491 | + "name": "Software" | |
2492 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -17,9 +17,9 @@ |
17 | 17 | "sizeY": 5.5, |
18 | 18 | "resources": [], |
19 | 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", | |
21 | - "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\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.multiParams) {\n multiParams = self.ctx.settings.multiParams;\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 = utils.guid();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\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 \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 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' <method> [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, 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\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\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 \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\"\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 multiParams = false;\nvar useRowStyleFunction = false;\nvar styleObj = {};\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.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n if (self.ctx.settings.useRowStyleFunction && self.ctx.settings.rowStyleFunction) {\n try {\n var style = self.ctx.settings.rowStyleFunction;\n styleObj = JSON.parse(style);\n if ((typeof styleObj !== \"object\")) {\n styleObj = null;\n throw new URIError(`${style === null ? 'null' : typeof style} instead of style object`);\n }\n else if (typeof styleObj === \"object\" && (typeof styleObj.length) === \"number\") {\n styleObj = null;\n throw new URIError('Array instead of style object');\n }\n }\n catch (e) {\n console.log(`Row style function in widget ` +\n `returns '${e}'. Please check your row style function.`); \n }\n useRowStyleFunction = self.ctx.settings.useRowStyleFunction;\n \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 cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\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 (styleObj && styleObj !== null) {\n terminal.css(styleObj);\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 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' <method> [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, 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\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\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 \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", | |
23 | 23 | "dataKeySettingsSchema": "{}\n", |
24 | 24 | "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
25 | 25 | } | ... | ... |
... | ... | @@ -79,6 +79,68 @@ CREATE TABLE IF NOT EXISTS ota_package ( |
79 | 79 | CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) |
80 | 80 | ); |
81 | 81 | |
82 | +CREATE TABLE IF NOT EXISTS oauth2_params ( | |
83 | + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, | |
84 | + enabled boolean, | |
85 | + tenant_id uuid, | |
86 | + created_time bigint NOT NULL | |
87 | +); | |
88 | + | |
89 | +CREATE TABLE IF NOT EXISTS oauth2_registration ( | |
90 | + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, | |
91 | + oauth2_params_id uuid NOT NULL, | |
92 | + created_time bigint NOT NULL, | |
93 | + additional_info varchar, | |
94 | + client_id varchar(255), | |
95 | + client_secret varchar(255), | |
96 | + authorization_uri varchar(255), | |
97 | + token_uri varchar(255), | |
98 | + scope varchar(255), | |
99 | + platforms varchar(255), | |
100 | + user_info_uri varchar(255), | |
101 | + user_name_attribute_name varchar(255), | |
102 | + jwk_set_uri varchar(255), | |
103 | + client_authentication_method varchar(255), | |
104 | + login_button_label varchar(255), | |
105 | + login_button_icon varchar(255), | |
106 | + allow_user_creation boolean, | |
107 | + activate_user boolean, | |
108 | + type varchar(31), | |
109 | + basic_email_attribute_key varchar(31), | |
110 | + basic_first_name_attribute_key varchar(31), | |
111 | + basic_last_name_attribute_key varchar(31), | |
112 | + basic_tenant_name_strategy varchar(31), | |
113 | + basic_tenant_name_pattern varchar(255), | |
114 | + basic_customer_name_pattern varchar(255), | |
115 | + basic_default_dashboard_name varchar(255), | |
116 | + basic_always_full_screen boolean, | |
117 | + custom_url varchar(255), | |
118 | + custom_username varchar(255), | |
119 | + custom_password varchar(255), | |
120 | + custom_send_token boolean, | |
121 | + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE | |
122 | +); | |
123 | + | |
124 | +CREATE TABLE IF NOT EXISTS oauth2_domain ( | |
125 | + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, | |
126 | + oauth2_params_id uuid NOT NULL, | |
127 | + created_time bigint NOT NULL, | |
128 | + domain_name varchar(255), | |
129 | + domain_scheme varchar(31), | |
130 | + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, | |
131 | + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) | |
132 | +); | |
133 | + | |
134 | +CREATE TABLE IF NOT EXISTS oauth2_mobile ( | |
135 | + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, | |
136 | + oauth2_params_id uuid NOT NULL, | |
137 | + created_time bigint NOT NULL, | |
138 | + pkg_name varchar(255), | |
139 | + callback_url_scheme varchar(255), | |
140 | + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, | |
141 | + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) | |
142 | +); | |
143 | + | |
82 | 144 | ALTER TABLE dashboard |
83 | 145 | ADD COLUMN IF NOT EXISTS image varchar(1000000); |
84 | 146 | ... | ... |
... | ... | @@ -204,6 +204,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
204 | 204 | syncSessionSet.add(key); |
205 | 205 | } |
206 | 206 | }); |
207 | + log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions); | |
207 | 208 | syncSessionSet.forEach(rpcSubscriptions::remove); |
208 | 209 | } |
209 | 210 | |
... | ... | @@ -517,7 +518,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
517 | 518 | } else { |
518 | 519 | SessionInfoMetaData sessionMD = sessions.get(sessionId); |
519 | 520 | if (sessionMD == null) { |
520 | - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); | |
521 | + sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); | |
521 | 522 | } |
522 | 523 | sessionMD.setSubscribedToAttributes(true); |
523 | 524 | log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); |
... | ... | @@ -538,7 +539,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
538 | 539 | } else { |
539 | 540 | SessionInfoMetaData sessionMD = sessions.get(sessionId); |
540 | 541 | if (sessionMD == null) { |
541 | - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); | |
542 | + sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); | |
542 | 543 | } |
543 | 544 | sessionMD.setSubscribedToRPC(true); |
544 | 545 | log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); | ... | ... |
... | ... | @@ -37,6 +37,8 @@ import org.springframework.util.StringUtils; |
37 | 37 | import org.springframework.web.util.UriComponents; |
38 | 38 | import org.springframework.web.util.UriComponentsBuilder; |
39 | 39 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
40 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
41 | +import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; | |
40 | 42 | import org.thingsboard.server.utils.MiscUtils; |
41 | 43 | |
42 | 44 | import javax.servlet.http.HttpServletRequest; |
... | ... | @@ -46,12 +48,13 @@ import java.security.NoSuchAlgorithmException; |
46 | 48 | import java.util.Base64; |
47 | 49 | import java.util.HashMap; |
48 | 50 | import java.util.Map; |
51 | +import java.util.UUID; | |
49 | 52 | |
50 | 53 | @Service |
51 | 54 | @Slf4j |
52 | 55 | public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { |
53 | - public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; | |
54 | - public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; | |
56 | + private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; | |
57 | + private static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; | |
55 | 58 | private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; |
56 | 59 | private static final char PATH_DELIMITER = '/'; |
57 | 60 | |
... | ... | @@ -63,6 +66,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
63 | 66 | @Autowired |
64 | 67 | private ClientRegistrationRepository clientRegistrationRepository; |
65 | 68 | |
69 | + @Autowired | |
70 | + private OAuth2Service oAuth2Service; | |
71 | + | |
66 | 72 | @Autowired(required = false) |
67 | 73 | private OAuth2Configuration oauth2Configuration; |
68 | 74 | |
... | ... | @@ -71,7 +77,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
71 | 77 | public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { |
72 | 78 | String registrationId = this.resolveRegistrationId(request); |
73 | 79 | String redirectUriAction = getAction(request, "login"); |
74 | - return resolve(request, registrationId, redirectUriAction); | |
80 | + String appPackage = getAppPackage(request); | |
81 | + return resolve(request, registrationId, redirectUriAction, appPackage); | |
75 | 82 | } |
76 | 83 | |
77 | 84 | @Override |
... | ... | @@ -80,7 +87,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
80 | 87 | return null; |
81 | 88 | } |
82 | 89 | String redirectUriAction = getAction(request, "authorize"); |
83 | - return resolve(request, registrationId, redirectUriAction); | |
90 | + String appPackage = getAppPackage(request); | |
91 | + return resolve(request, registrationId, redirectUriAction, appPackage); | |
84 | 92 | } |
85 | 93 | |
86 | 94 | private String getAction(HttpServletRequest request, String defaultAction) { |
... | ... | @@ -91,8 +99,12 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
91 | 99 | return action; |
92 | 100 | } |
93 | 101 | |
102 | + private String getAppPackage(HttpServletRequest request) { | |
103 | + return request.getParameter("pkg"); | |
104 | + } | |
105 | + | |
94 | 106 | @SuppressWarnings("deprecation") |
95 | - private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { | |
107 | + private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage) { | |
96 | 108 | if (registrationId == null) { |
97 | 109 | return null; |
98 | 110 | } |
... | ... | @@ -104,6 +116,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza |
104 | 116 | |
105 | 117 | Map<String, Object> attributes = new HashMap<>(); |
106 | 118 | attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); |
119 | + if (!StringUtils.isEmpty(appPackage)) { | |
120 | + String callbackUrlScheme = this.oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), appPackage); | |
121 | + if (StringUtils.isEmpty(callbackUrlScheme)) { | |
122 | + throw new IllegalArgumentException("Invalid package: " + appPackage + ". No package info found for Client Registration."); | |
123 | + } else { | |
124 | + attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme); | |
125 | + } | |
126 | + } | |
107 | 127 | |
108 | 128 | OAuth2AuthorizationRequest.Builder builder; |
109 | 129 | if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { | ... | ... |
... | ... | @@ -22,12 +22,15 @@ import org.springframework.security.access.prepost.PreAuthorize; |
22 | 22 | import org.springframework.web.bind.annotation.RequestBody; |
23 | 23 | import org.springframework.web.bind.annotation.RequestMapping; |
24 | 24 | import org.springframework.web.bind.annotation.RequestMethod; |
25 | +import org.springframework.web.bind.annotation.RequestParam; | |
25 | 26 | import org.springframework.web.bind.annotation.ResponseBody; |
26 | 27 | import org.springframework.web.bind.annotation.ResponseStatus; |
27 | 28 | import org.springframework.web.bind.annotation.RestController; |
29 | +import org.thingsboard.server.common.data.StringUtils; | |
28 | 30 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
29 | 31 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
30 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; | |
32 | +import org.thingsboard.server.common.data.oauth2.OAuth2Info; | |
33 | +import org.thingsboard.server.common.data.oauth2.PlatformType; | |
31 | 34 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
32 | 35 | import org.thingsboard.server.queue.util.TbCoreComponent; |
33 | 36 | import org.thingsboard.server.service.security.permission.Operation; |
... | ... | @@ -49,7 +52,9 @@ public class OAuth2Controller extends BaseController { |
49 | 52 | |
50 | 53 | @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) |
51 | 54 | @ResponseBody |
52 | - public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { | |
55 | + public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request, | |
56 | + @RequestParam(required = false) String pkgName, | |
57 | + @RequestParam(required = false) String platform) throws ThingsboardException { | |
53 | 58 | try { |
54 | 59 | if (log.isDebugEnabled()) { |
55 | 60 | log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); |
... | ... | @@ -59,7 +64,13 @@ public class OAuth2Controller extends BaseController { |
59 | 64 | log.debug("Header: {} {}", header, request.getHeader(header)); |
60 | 65 | } |
61 | 66 | } |
62 | - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request)); | |
67 | + PlatformType platformType = null; | |
68 | + if (StringUtils.isNotEmpty(platform)) { | |
69 | + try { | |
70 | + platformType = PlatformType.valueOf(platform); | |
71 | + } catch (Exception e) {} | |
72 | + } | |
73 | + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); | |
63 | 74 | } catch (Exception e) { |
64 | 75 | throw handleException(e); |
65 | 76 | } |
... | ... | @@ -68,10 +79,10 @@ public class OAuth2Controller extends BaseController { |
68 | 79 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
69 | 80 | @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") |
70 | 81 | @ResponseBody |
71 | - public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { | |
82 | + public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { | |
72 | 83 | try { |
73 | 84 | accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); |
74 | - return oAuth2Service.findOAuth2Params(); | |
85 | + return oAuth2Service.findOAuth2Info(); | |
75 | 86 | } catch (Exception e) { |
76 | 87 | throw handleException(e); |
77 | 88 | } |
... | ... | @@ -80,11 +91,11 @@ public class OAuth2Controller extends BaseController { |
80 | 91 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
81 | 92 | @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) |
82 | 93 | @ResponseStatus(value = HttpStatus.OK) |
83 | - public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { | |
94 | + public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { | |
84 | 95 | try { |
85 | 96 | accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); |
86 | - oAuth2Service.saveOAuth2Params(oauth2Params); | |
87 | - return oAuth2Service.findOAuth2Params(); | |
97 | + oAuth2Service.saveOAuth2Info(oauth2Info); | |
98 | + return oAuth2Service.findOAuth2Info(); | |
88 | 99 | } catch (Exception e) { |
89 | 100 | throw handleException(e); |
90 | 101 | } | ... | ... |
... | ... | @@ -199,6 +199,7 @@ public class ThingsboardInstallService { |
199 | 199 | databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); |
200 | 200 | |
201 | 201 | dataUpdateService.updateData("3.2.2"); |
202 | + systemDataLoaderService.createOAuth2Templates(); | |
202 | 203 | |
203 | 204 | log.info("Updating system data..."); |
204 | 205 | systemDataLoaderService.updateSystemWidgets(); | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
24 | 24 | import org.springframework.context.annotation.Profile; |
25 | 25 | import org.springframework.stereotype.Service; |
26 | +import org.thingsboard.common.util.JacksonUtil; | |
26 | 27 | import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; |
27 | 28 | import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; |
28 | 29 | import org.thingsboard.server.common.data.EntityView; |
... | ... | @@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.id.TenantId; |
35 | 36 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
36 | 37 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
37 | 38 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
39 | +import org.thingsboard.server.common.data.oauth2.OAuth2Info; | |
40 | +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; | |
38 | 41 | import org.thingsboard.server.common.data.page.PageData; |
39 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
40 | 43 | import org.thingsboard.server.common.data.page.TimePageLink; |
... | ... | @@ -45,10 +48,11 @@ import org.thingsboard.server.dao.alarm.AlarmDao; |
45 | 48 | import org.thingsboard.server.dao.alarm.AlarmService; |
46 | 49 | import org.thingsboard.server.dao.entity.EntityService; |
47 | 50 | import org.thingsboard.server.dao.entityview.EntityViewService; |
51 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
52 | +import org.thingsboard.server.dao.oauth2.OAuth2Utils; | |
48 | 53 | import org.thingsboard.server.dao.rule.RuleChainService; |
49 | 54 | import org.thingsboard.server.dao.tenant.TenantService; |
50 | 55 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
51 | -import org.thingsboard.common.util.JacksonUtil; | |
52 | 56 | import org.thingsboard.server.service.install.InstallScripts; |
53 | 57 | |
54 | 58 | import java.util.ArrayList; |
... | ... | @@ -88,6 +92,9 @@ public class DefaultDataUpdateService implements DataUpdateService { |
88 | 92 | @Autowired |
89 | 93 | private AlarmDao alarmDao; |
90 | 94 | |
95 | + @Autowired | |
96 | + private OAuth2Service oAuth2Service; | |
97 | + | |
91 | 98 | @Override |
92 | 99 | public void updateData(String fromVersion) throws Exception { |
93 | 100 | switch (fromVersion) { |
... | ... | @@ -107,6 +114,7 @@ public class DefaultDataUpdateService implements DataUpdateService { |
107 | 114 | log.info("Updating data from version 3.2.2 to 3.3.0 ..."); |
108 | 115 | tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); |
109 | 116 | tenantsAlarmsCustomerUpdater.updateEntities(null); |
117 | + updateOAuth2Params(); | |
110 | 118 | break; |
111 | 119 | default: |
112 | 120 | throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); |
... | ... | @@ -362,4 +370,20 @@ public class DefaultDataUpdateService implements DataUpdateService { |
362 | 370 | } |
363 | 371 | } |
364 | 372 | |
373 | + private void updateOAuth2Params() { | |
374 | + try { | |
375 | + OAuth2ClientsParams oauth2ClientsParams = oAuth2Service.findOAuth2Params(); | |
376 | + if (!oauth2ClientsParams.getDomainsParams().isEmpty()) { | |
377 | + log.info("Updating OAuth2 parameters ..."); | |
378 | + OAuth2Info oAuth2Info = OAuth2Utils.clientParamsToOAuth2Info(oauth2ClientsParams); | |
379 | + oAuth2Service.saveOAuth2Info(oAuth2Info); | |
380 | + oAuth2Service.saveOAuth2Params(new OAuth2ClientsParams(false, Collections.emptyList())); | |
381 | + log.info("Successfully updated OAuth2 parameters!"); | |
382 | + } | |
383 | + } | |
384 | + catch (Exception e) { | |
385 | + log.error("Failed to update OAuth2 parameters", e); | |
386 | + } | |
387 | + } | |
388 | + | |
365 | 389 | } | ... | ... |
... | ... | @@ -72,15 +72,15 @@ public class DefaultTbResourceService implements TbResourceService { |
72 | 72 | if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { |
73 | 73 | try { |
74 | 74 | List<ObjectModel> objectModels = |
75 | - ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
75 | + ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
76 | 76 | if (!objectModels.isEmpty()) { |
77 | 77 | ObjectModel objectModel = objectModels.get(0); |
78 | 78 | |
79 | - String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.getVersion(); | |
79 | + String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.version; | |
80 | 80 | String name = objectModel.name; |
81 | 81 | resource.setResourceKey(resourceKey); |
82 | 82 | if (resource.getId() == null) { |
83 | - resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.getVersion()); | |
83 | + resource.setTitle(name + " id=" + objectModel.id + " v" + objectModel.version); | |
84 | 84 | } |
85 | 85 | resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); |
86 | 86 | } else { |
... | ... | @@ -176,7 +176,7 @@ public class DefaultTbResourceService implements TbResourceService { |
176 | 176 | try { |
177 | 177 | DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); |
178 | 178 | List<ObjectModel> objectModels = |
179 | - ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
179 | + ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); | |
180 | 180 | if (objectModels.size() == 0) { |
181 | 181 | return null; |
182 | 182 | } else { | ... | ... |
... | ... | @@ -33,8 +33,8 @@ import org.thingsboard.server.common.data.id.CustomerId; |
33 | 33 | import org.thingsboard.server.common.data.id.DashboardId; |
34 | 34 | import org.thingsboard.server.common.data.id.IdBased; |
35 | 35 | import org.thingsboard.server.common.data.id.TenantId; |
36 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
37 | 36 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
37 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
38 | 38 | import org.thingsboard.server.common.data.page.PageData; |
39 | 39 | import org.thingsboard.server.common.data.page.PageLink; |
40 | 40 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
... | ... | @@ -93,9 +93,9 @@ public abstract class AbstractOAuth2ClientMapper { |
93 | 93 | |
94 | 94 | private final Lock userCreationLock = new ReentrantLock(); |
95 | 95 | |
96 | - protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { | |
96 | + protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2Registration registration) { | |
97 | 97 | |
98 | - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | |
98 | + OAuth2MapperConfig config = registration.getMapperConfig(); | |
99 | 99 | |
100 | 100 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); |
101 | 101 | |
... | ... | @@ -139,9 +139,9 @@ public abstract class AbstractOAuth2ClientMapper { |
139 | 139 | } |
140 | 140 | } |
141 | 141 | |
142 | - if (clientRegistration.getAdditionalInfo() != null && | |
143 | - clientRegistration.getAdditionalInfo().has("providerName")) { | |
144 | - additionalInfo.put("authProviderName", clientRegistration.getAdditionalInfo().get("providerName").asText()); | |
142 | + if (registration.getAdditionalInfo() != null && | |
143 | + registration.getAdditionalInfo().has("providerName")) { | |
144 | + additionalInfo.put("authProviderName", registration.getAdditionalInfo().get("providerName").asText()); | |
145 | 145 | } |
146 | 146 | |
147 | 147 | user.setAdditionalInfo(additionalInfo); | ... | ... |
... | ... | @@ -18,8 +18,8 @@ package org.thingsboard.server.service.security.auth.oauth2; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
20 | 20 | import org.springframework.stereotype.Service; |
21 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
22 | 21 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
22 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
23 | 23 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
24 | 24 | import org.thingsboard.server.service.security.model.SecurityUser; |
25 | 25 | |
... | ... | @@ -30,12 +30,12 @@ import java.util.Map; |
30 | 30 | public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
31 | 31 | |
32 | 32 | @Override |
33 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { | |
34 | - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | |
33 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { | |
34 | + OAuth2MapperConfig config = registration.getMapperConfig(); | |
35 | 35 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
36 | 36 | String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
37 | 37 | OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); |
38 | 38 | |
39 | - return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); | |
39 | + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); | |
40 | 40 | } |
41 | 41 | } | ... | ... |
... | ... | @@ -23,9 +23,9 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic |
23 | 23 | import org.springframework.stereotype.Service; |
24 | 24 | import org.springframework.util.StringUtils; |
25 | 25 | import org.springframework.web.client.RestTemplate; |
26 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
27 | 26 | import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; |
28 | 27 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
28 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
29 | 29 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
30 | 30 | import org.thingsboard.server.service.security.model.SecurityUser; |
31 | 31 | |
... | ... | @@ -39,10 +39,10 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme |
39 | 39 | private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); |
40 | 40 | |
41 | 41 | @Override |
42 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { | |
43 | - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | |
42 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { | |
43 | + OAuth2MapperConfig config = registration.getMapperConfig(); | |
44 | 44 | OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); |
45 | - return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); | |
45 | + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); | |
46 | 46 | } |
47 | 47 | |
48 | 48 | private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { | ... | ... |
... | ... | @@ -23,8 +23,8 @@ import org.springframework.boot.web.client.RestTemplateBuilder; |
23 | 23 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | 25 | import org.springframework.web.client.RestTemplate; |
26 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
27 | 26 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
27 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
28 | 28 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
29 | 29 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
30 | 30 | import org.thingsboard.server.service.security.model.SecurityUser; |
... | ... | @@ -46,13 +46,13 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme |
46 | 46 | private OAuth2Configuration oAuth2Configuration; |
47 | 47 | |
48 | 48 | @Override |
49 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { | |
50 | - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | |
49 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { | |
50 | + OAuth2MapperConfig config = registration.getMapperConfig(); | |
51 | 51 | Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); |
52 | 52 | String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); |
53 | 53 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
54 | 54 | OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); |
55 | - return getOrCreateSecurityUserFromOAuth2User(oAuth2User, clientRegistration); | |
55 | + return getOrCreateSecurityUserFromOAuth2User(oAuth2User, registration); | |
56 | 56 | } |
57 | 57 | |
58 | 58 | private synchronized String getEmail(String emailUrl, String oauth2Token) { | ... | ... |
... | ... | @@ -16,9 +16,10 @@ |
16 | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | |
18 | 18 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
19 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
19 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
20 | +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; | |
20 | 21 | import org.thingsboard.server.service.security.model.SecurityUser; |
21 | 22 | |
22 | 23 | public interface OAuth2ClientMapper { |
23 | - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration); | |
24 | + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration); | |
24 | 25 | } | ... | ... |
... | ... | @@ -18,8 +18,10 @@ package org.thingsboard.server.service.security.auth.oauth2; |
18 | 18 | import org.springframework.beans.factory.annotation.Autowired; |
19 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
20 | 20 | import org.springframework.security.core.AuthenticationException; |
21 | +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; | |
21 | 22 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; |
22 | 23 | import org.springframework.stereotype.Component; |
24 | +import org.thingsboard.server.common.data.StringUtils; | |
23 | 25 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
25 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -51,9 +53,19 @@ public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationF |
51 | 53 | public void onAuthenticationFailure(HttpServletRequest request, |
52 | 54 | HttpServletResponse response, AuthenticationException exception) |
53 | 55 | throws IOException, ServletException { |
54 | - String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); | |
56 | + String baseUrl; | |
57 | + String errorPrefix; | |
58 | + OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request); | |
59 | + String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME); | |
60 | + if (!StringUtils.isEmpty(callbackUrlScheme)) { | |
61 | + baseUrl = callbackUrlScheme + ":"; | |
62 | + errorPrefix = "/?error="; | |
63 | + } else { | |
64 | + baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); | |
65 | + errorPrefix = "/login?loginError="; | |
66 | + } | |
55 | 67 | httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); |
56 | - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + | |
68 | + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix + | |
57 | 69 | URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); |
58 | 70 | } |
59 | 71 | } | ... | ... |
... | ... | @@ -20,12 +20,14 @@ import org.springframework.security.core.Authentication; |
20 | 20 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; |
21 | 21 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; |
22 | 22 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
23 | +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; | |
23 | 24 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; |
24 | 25 | import org.springframework.stereotype.Component; |
26 | +import org.thingsboard.server.common.data.StringUtils; | |
25 | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
26 | 28 | import org.thingsboard.server.common.data.id.EntityId; |
27 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
28 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
30 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
29 | 31 | import org.thingsboard.server.common.data.security.model.JwtToken; |
30 | 32 | import org.thingsboard.server.dao.oauth2.OAuth2Service; |
31 | 33 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; |
... | ... | @@ -72,17 +74,24 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS |
72 | 74 | public void onAuthenticationSuccess(HttpServletRequest request, |
73 | 75 | HttpServletResponse response, |
74 | 76 | Authentication authentication) throws IOException { |
75 | - String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); | |
77 | + OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request); | |
78 | + String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME); | |
79 | + String baseUrl; | |
80 | + if (!StringUtils.isEmpty(callbackUrlScheme)) { | |
81 | + baseUrl = callbackUrlScheme + ":"; | |
82 | + } else { | |
83 | + baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); | |
84 | + } | |
76 | 85 | try { |
77 | 86 | OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; |
78 | 87 | |
79 | - OAuth2ClientRegistrationInfo clientRegistration = oAuth2Service.findClientRegistrationInfo(UUID.fromString(token.getAuthorizedClientRegistrationId())); | |
88 | + OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId())); | |
80 | 89 | OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient( |
81 | 90 | token.getAuthorizedClientRegistrationId(), |
82 | 91 | token.getPrincipal().getName()); |
83 | - OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); | |
92 | + OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType()); | |
84 | 93 | SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), |
85 | - clientRegistration); | |
94 | + registration); | |
86 | 95 | |
87 | 96 | JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); |
88 | 97 | JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); |
... | ... | @@ -91,7 +100,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS |
91 | 100 | getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); |
92 | 101 | } catch (Exception e) { |
93 | 102 | clearAuthenticationAttributes(request, response); |
94 | - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + | |
103 | + String errorPrefix; | |
104 | + if (!StringUtils.isEmpty(callbackUrlScheme)) { | |
105 | + errorPrefix = "/?error="; | |
106 | + } else { | |
107 | + errorPrefix = "/login?loginError="; | |
108 | + } | |
109 | + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix + | |
95 | 110 | URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); |
96 | 111 | } |
97 | 112 | } | ... | ... |
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 | +package org.thingsboard.server.service.security.auth.oauth2; | |
17 | + | |
18 | +public interface TbOAuth2ParameterNames { | |
19 | + | |
20 | + String CALLBACK_URL_SCHEME = "callback_url_scheme"; | |
21 | + | |
22 | +} | ... | ... |
... | ... | @@ -656,7 +656,7 @@ transport: |
656 | 656 | bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" |
657 | 657 | bind_port: "${LWM2M_BIND_PORT_BS:5687}" |
658 | 658 | security: |
659 | - bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" | |
659 | + bind_address: "${LWM2M_BIND_ADDRESS_SECURITY_BS:0.0.0.0}" | |
660 | 660 | bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" |
661 | 661 | # Only for RPK: Public & Private Key. If the keystore file is missing or not working |
662 | 662 | public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" | ... | ... |
... | ... | @@ -60,6 +60,53 @@ import java.util.concurrent.ScheduledExecutorService; |
60 | 60 | @DaoSqlTest |
61 | 61 | public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { |
62 | 62 | |
63 | + protected final String TRANSPORT_CONFIGURATION = "{\n" + | |
64 | + " \"type\": \"LWM2M\",\n" + | |
65 | + " \"observeAttr\": {\n" + | |
66 | + " \"keyName\": {\n" + | |
67 | + " \"/3_1.0/0/9\": \"batteryLevel\"\n" + | |
68 | + " },\n" + | |
69 | + " \"observe\": [],\n" + | |
70 | + " \"attribute\": [\n" + | |
71 | + " ],\n" + | |
72 | + " \"telemetry\": [\n" + | |
73 | + " \"/3_1.0/0/9\"\n" + | |
74 | + " ],\n" + | |
75 | + " \"attributeLwm2m\": {}\n" + | |
76 | + " },\n" + | |
77 | + " \"bootstrap\": {\n" + | |
78 | + " \"servers\": {\n" + | |
79 | + " \"binding\": \"U\",\n" + | |
80 | + " \"shortId\": 123,\n" + | |
81 | + " \"lifetime\": 300,\n" + | |
82 | + " \"notifIfDisabled\": true,\n" + | |
83 | + " \"defaultMinPeriod\": 1\n" + | |
84 | + " },\n" + | |
85 | + " \"lwm2mServer\": {\n" + | |
86 | + " \"host\": \"localhost\",\n" + | |
87 | + " \"port\": 5686,\n" + | |
88 | + " \"serverId\": 123,\n" + | |
89 | + " \"serverPublicKey\": \"\",\n" + | |
90 | + " \"bootstrapServerIs\": false,\n" + | |
91 | + " \"clientHoldOffTime\": 1,\n" + | |
92 | + " \"bootstrapServerAccountTimeout\": 0\n" + | |
93 | + " },\n" + | |
94 | + " \"bootstrapServer\": {\n" + | |
95 | + " \"host\": \"localhost\",\n" + | |
96 | + " \"port\": 5687,\n" + | |
97 | + " \"serverId\": 111,\n" + | |
98 | + " \"securityMode\": \"NO_SEC\",\n" + | |
99 | + " \"serverPublicKey\": \"\",\n" + | |
100 | + " \"bootstrapServerIs\": true,\n" + | |
101 | + " \"clientHoldOffTime\": 1,\n" + | |
102 | + " \"bootstrapServerAccountTimeout\": 0\n" + | |
103 | + " }\n" + | |
104 | + " },\n" + | |
105 | + " \"clientLwM2mSettings\": {\n" + | |
106 | + " \"clientOnlyObserveAfterConnect\": 1\n" + | |
107 | + " }\n" + | |
108 | + "}"; | |
109 | + | |
63 | 110 | protected DeviceProfile deviceProfile; |
64 | 111 | protected ScheduledExecutorService executor; |
65 | 112 | protected TbTestWebSocketClient wsClient; | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import org.junit.Assert; |
22 | 22 | import org.junit.Test; |
23 | 23 | import org.thingsboard.common.util.JacksonUtil; |
24 | 24 | import org.thingsboard.server.common.data.Device; |
25 | +import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; | |
25 | 26 | import org.thingsboard.server.common.data.query.EntityData; |
26 | 27 | import org.thingsboard.server.common.data.query.EntityDataPageLink; |
27 | 28 | import org.thingsboard.server.common.data.query.EntityDataQuery; |
... | ... | @@ -36,7 +37,6 @@ import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; |
36 | 37 | import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd; |
37 | 38 | import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient; |
38 | 39 | import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials; |
39 | -import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; | |
40 | 40 | |
41 | 41 | import java.util.Collections; |
42 | 42 | import java.util.List; |
... | ... | @@ -46,60 +46,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. |
46 | 46 | |
47 | 47 | public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
48 | 48 | |
49 | - protected final String TRANSPORT_CONFIGURATION = "{\n" + | |
50 | - " \"type\": \"LWM2M\",\n" + | |
51 | - " \"observeAttr\": {\n" + | |
52 | - " \"keyName\": {\n" + | |
53 | - " \"/3_1.0/0/9\": \"batteryLevel\"\n" + | |
54 | - " },\n" + | |
55 | - " \"observe\": [],\n" + | |
56 | - " \"attribute\": [\n" + | |
57 | - " ],\n" + | |
58 | - " \"telemetry\": [\n" + | |
59 | - " \"/3_1.0/0/9\"\n" + | |
60 | - " ],\n" + | |
61 | - " \"attributeLwm2m\": {}\n" + | |
62 | - " },\n" + | |
63 | - " \"bootstrap\": {\n" + | |
64 | - " \"servers\": {\n" + | |
65 | - " \"binding\": \"UQ\",\n" + | |
66 | - " \"shortId\": 123,\n" + | |
67 | - " \"lifetime\": 300,\n" + | |
68 | - " \"notifIfDisabled\": true,\n" + | |
69 | - " \"defaultMinPeriod\": 1\n" + | |
70 | - " },\n" + | |
71 | - " \"lwm2mServer\": {\n" + | |
72 | - " \"host\": \"localhost\",\n" + | |
73 | - " \"port\": 5685,\n" + | |
74 | - " \"serverId\": 123,\n" + | |
75 | - " \"securityMode\": \"NO_SEC\",\n" + | |
76 | - " \"serverPublicKey\": \"\",\n" + | |
77 | - " \"bootstrapServerIs\": false,\n" + | |
78 | - " \"clientHoldOffTime\": 1,\n" + | |
79 | - " \"bootstrapServerAccountTimeout\": 0\n" + | |
80 | - " },\n" + | |
81 | - " \"bootstrapServer\": {\n" + | |
82 | - " \"host\": \"localhost\",\n" + | |
83 | - " \"port\": 5687,\n" + | |
84 | - " \"serverId\": 111,\n" + | |
85 | - " \"securityMode\": \"NO_SEC\",\n" + | |
86 | - " \"serverPublicKey\": \"\",\n" + | |
87 | - " \"bootstrapServerIs\": true,\n" + | |
88 | - " \"clientHoldOffTime\": 1,\n" + | |
89 | - " \"bootstrapServerAccountTimeout\": 0\n" + | |
90 | - " }\n" + | |
91 | - " },\n" + | |
92 | - " \"clientLwM2mSettings\": {\n" + | |
93 | - " \"clientOnlyObserveAfterConnect\": 1\n" + | |
94 | - " }\n" + | |
95 | - "}"; | |
96 | - | |
97 | - private final int port = 5685; | |
98 | - private final Security security = noSec("coap://localhost:" + port, 123); | |
99 | - private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port)); | |
49 | + private final int PORT = 5685; | |
50 | + private final Security SECURITY = noSec("coap://localhost:" + PORT, 123); | |
51 | + private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); | |
52 | + private final String ENDPOINT = "deviceAEndpoint"; | |
100 | 53 | |
101 | 54 | @NotNull |
102 | - private Device createDevice(String deviceAEndpoint) throws Exception { | |
55 | + private Device createDevice() throws Exception { | |
103 | 56 | Device device = new Device(); |
104 | 57 | device.setName("Device A"); |
105 | 58 | device.setDeviceProfileId(deviceProfile.getId()); |
... | ... | @@ -114,7 +67,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
114 | 67 | |
115 | 68 | LwM2MCredentials noSecCredentials = new LwM2MCredentials(); |
116 | 69 | NoSecClientCredentials clientCredentials = new NoSecClientCredentials(); |
117 | - clientCredentials.setEndpoint(deviceAEndpoint); | |
70 | + clientCredentials.setEndpoint(ENDPOINT); | |
118 | 71 | noSecCredentials.setClient(clientCredentials); |
119 | 72 | deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials)); |
120 | 73 | doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk()); |
... | ... | @@ -125,9 +78,7 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
125 | 78 | public void testConnectAndObserveTelemetry() throws Exception { |
126 | 79 | createDeviceProfile(TRANSPORT_CONFIGURATION); |
127 | 80 | |
128 | - String deviceAEndpoint = "deviceAEndpoint"; | |
129 | - | |
130 | - Device device = createDevice(deviceAEndpoint); | |
81 | + Device device = createDevice(); | |
131 | 82 | |
132 | 83 | SingleEntityFilter sef = new SingleEntityFilter(); |
133 | 84 | sef.setSingleEntity(device.getId()); |
... | ... | @@ -144,8 +95,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
144 | 95 | wsClient.waitForReply(); |
145 | 96 | |
146 | 97 | wsClient.registerWaitForUpdate(); |
147 | - LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint); | |
148 | - client.init(security, coapConfig); | |
98 | + LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT); | |
99 | + client.init(SECURITY, COAP_CONFIG); | |
149 | 100 | String msg = wsClient.waitForUpdate(); |
150 | 101 | |
151 | 102 | EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); | ... | ... |
... | ... | @@ -47,54 +47,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. |
47 | 47 | |
48 | 48 | public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
49 | 49 | |
50 | - protected final String TRANSPORT_CONFIGURATION = "{\n" + | |
51 | - " \"type\": \"LWM2M\",\n" + | |
52 | - " \"observeAttr\": {\n" + | |
53 | - " \"keyName\": {\n" + | |
54 | - " \"/3_1.0/0/9\": \"batteryLevel\"\n" + | |
55 | - " },\n" + | |
56 | - " \"observe\": [],\n" + | |
57 | - " \"attribute\": [\n" + | |
58 | - " ],\n" + | |
59 | - " \"telemetry\": [\n" + | |
60 | - " \"/3_1.0/0/9\"\n" + | |
61 | - " ],\n" + | |
62 | - " \"attributeLwm2m\": {}\n" + | |
63 | - " },\n" + | |
64 | - " \"bootstrap\": {\n" + | |
65 | - " \"servers\": {\n" + | |
66 | - " \"binding\": \"UQ\",\n" + | |
67 | - " \"shortId\": 123,\n" + | |
68 | - " \"lifetime\": 300,\n" + | |
69 | - " \"notifIfDisabled\": true,\n" + | |
70 | - " \"defaultMinPeriod\": 1\n" + | |
71 | - " },\n" + | |
72 | - " \"lwm2mServer\": {\n" + | |
73 | - " \"host\": \"localhost\",\n" + | |
74 | - " \"port\": 5686,\n" + | |
75 | - " \"serverId\": 123,\n" + | |
76 | - " \"serverPublicKey\": \"\",\n" + | |
77 | - " \"bootstrapServerIs\": false,\n" + | |
78 | - " \"clientHoldOffTime\": 1,\n" + | |
79 | - " \"bootstrapServerAccountTimeout\": 0\n" + | |
80 | - " },\n" + | |
81 | - " \"bootstrapServer\": {\n" + | |
82 | - " \"host\": \"localhost\",\n" + | |
83 | - " \"port\": 5687,\n" + | |
84 | - " \"serverId\": 111,\n" + | |
85 | - " \"securityMode\": \"NO_SEC\",\n" + | |
86 | - " \"serverPublicKey\": \"\",\n" + | |
87 | - " \"bootstrapServerIs\": true,\n" + | |
88 | - " \"clientHoldOffTime\": 1,\n" + | |
89 | - " \"bootstrapServerAccountTimeout\": 0\n" + | |
90 | - " }\n" + | |
91 | - " },\n" + | |
92 | - " \"clientLwM2mSettings\": {\n" + | |
93 | - " \"clientOnlyObserveAfterConnect\": 1\n" + | |
94 | - " }\n" + | |
95 | - "}"; | |
96 | - | |
97 | - | |
98 | 50 | private final int port = 5686; |
99 | 51 | private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port)); |
100 | 52 | private final String endpoint = "deviceAEndpoint"; | ... | ... |
... | ... | @@ -17,40 +17,37 @@ package org.thingsboard.server.transport.lwm2m.client; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.eclipse.californium.core.network.CoapEndpoint; | |
20 | 21 | import org.eclipse.californium.core.network.config.NetworkConfig; |
21 | -import org.eclipse.californium.elements.Connector; | |
22 | +import org.eclipse.californium.core.observe.ObservationStore; | |
22 | 23 | import org.eclipse.californium.scandium.DTLSConnector; |
23 | 24 | import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
24 | -import org.eclipse.californium.scandium.dtls.ClientHandshaker; | |
25 | -import org.eclipse.californium.scandium.dtls.DTLSSession; | |
26 | -import org.eclipse.californium.scandium.dtls.HandshakeException; | |
27 | -import org.eclipse.californium.scandium.dtls.Handshaker; | |
28 | -import org.eclipse.californium.scandium.dtls.ResumingClientHandshaker; | |
29 | -import org.eclipse.californium.scandium.dtls.ResumingServerHandshaker; | |
30 | -import org.eclipse.californium.scandium.dtls.ServerHandshaker; | |
31 | -import org.eclipse.californium.scandium.dtls.SessionAdapter; | |
32 | 25 | import org.eclipse.leshan.client.californium.LeshanClient; |
33 | 26 | import org.eclipse.leshan.client.californium.LeshanClientBuilder; |
34 | 27 | import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; |
35 | 28 | import org.eclipse.leshan.client.object.Security; |
36 | 29 | import org.eclipse.leshan.client.object.Server; |
37 | 30 | import org.eclipse.leshan.client.observer.LwM2mClientObserver; |
31 | +import org.eclipse.leshan.client.resource.DummyInstanceEnabler; | |
38 | 32 | import org.eclipse.leshan.client.resource.ObjectsInitializer; |
39 | 33 | import org.eclipse.leshan.client.servers.ServerIdentity; |
34 | +import org.eclipse.leshan.core.LwM2mId; | |
40 | 35 | import org.eclipse.leshan.core.ResponseCode; |
41 | -import org.eclipse.leshan.core.californium.DefaultEndpointFactory; | |
36 | +import org.eclipse.leshan.core.californium.EndpointFactory; | |
37 | +import org.eclipse.leshan.core.model.InvalidDDFFileException; | |
42 | 38 | import org.eclipse.leshan.core.model.LwM2mModel; |
43 | 39 | import org.eclipse.leshan.core.model.ObjectLoader; |
44 | 40 | import org.eclipse.leshan.core.model.ObjectModel; |
45 | 41 | import org.eclipse.leshan.core.model.StaticModel; |
46 | 42 | import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; |
47 | 43 | import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; |
48 | -import org.eclipse.leshan.core.request.BindingMode; | |
49 | 44 | import org.eclipse.leshan.core.request.BootstrapRequest; |
50 | 45 | import org.eclipse.leshan.core.request.DeregisterRequest; |
51 | 46 | import org.eclipse.leshan.core.request.RegisterRequest; |
52 | 47 | import org.eclipse.leshan.core.request.UpdateRequest; |
53 | 48 | |
49 | +import java.io.IOException; | |
50 | +import java.net.InetSocketAddress; | |
54 | 51 | import java.util.ArrayList; |
55 | 52 | import java.util.List; |
56 | 53 | import java.util.concurrent.ScheduledExecutorService; |
... | ... | @@ -67,7 +64,7 @@ public class LwM2MTestClient { |
67 | 64 | private final String endpoint; |
68 | 65 | private LeshanClient client; |
69 | 66 | |
70 | - public void init(Security security, NetworkConfig coapConfig) { | |
67 | + public void init(Security security, NetworkConfig coapConfig) throws InvalidDDFFileException, IOException { | |
71 | 68 | String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"}; |
72 | 69 | List<ObjectModel> models = new ArrayList<>(); |
73 | 70 | for (String resourceName : resources) { |
... | ... | @@ -76,82 +73,51 @@ public class LwM2MTestClient { |
76 | 73 | LwM2mModel model = new StaticModel(models); |
77 | 74 | ObjectsInitializer initializer = new ObjectsInitializer(model); |
78 | 75 | initializer.setInstancesForObject(SECURITY, security); |
79 | - initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false)); | |
76 | + initializer.setInstancesForObject(SERVER, new Server(123, 300)); | |
80 | 77 | initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice()); |
78 | + initializer.setClassForObject(LwM2mId.ACCESS_CONTROL, DummyInstanceEnabler.class); | |
81 | 79 | |
82 | 80 | DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); |
83 | 81 | dtlsConfig.setRecommendedCipherSuitesOnly(true); |
82 | + dtlsConfig.setClientOnly(); | |
84 | 83 | |
85 | 84 | DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory(); |
86 | 85 | engineFactory.setReconnectOnUpdate(false); |
87 | 86 | engineFactory.setResumeOnConnect(true); |
88 | 87 | |
89 | - DefaultEndpointFactory endpointFactory = new DefaultEndpointFactory(endpoint) { | |
88 | + EndpointFactory endpointFactory = new EndpointFactory() { | |
89 | + | |
90 | 90 | @Override |
91 | - protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) { | |
92 | - | |
93 | - return new DTLSConnector(dtlsConfig) { | |
94 | - @Override | |
95 | - protected void onInitializeHandshaker(Handshaker handshaker) { | |
96 | - handshaker.addSessionListener(new SessionAdapter() { | |
97 | - | |
98 | - @Override | |
99 | - public void handshakeStarted(Handshaker handshaker) throws HandshakeException { | |
100 | - if (handshaker instanceof ServerHandshaker) { | |
101 | - log.info("DTLS Full Handshake initiated by server : STARTED ..."); | |
102 | - } else if (handshaker instanceof ResumingServerHandshaker) { | |
103 | - log.info("DTLS abbreviated Handshake initiated by server : STARTED ..."); | |
104 | - } else if (handshaker instanceof ClientHandshaker) { | |
105 | - log.info("DTLS Full Handshake initiated by client : STARTED ..."); | |
106 | - } else if (handshaker instanceof ResumingClientHandshaker) { | |
107 | - log.info("DTLS abbreviated Handshake initiated by client : STARTED ..."); | |
108 | - } | |
109 | - } | |
110 | - | |
111 | - @Override | |
112 | - public void sessionEstablished(Handshaker handshaker, DTLSSession establishedSession) | |
113 | - throws HandshakeException { | |
114 | - if (handshaker instanceof ServerHandshaker) { | |
115 | - log.info("DTLS Full Handshake initiated by server : SUCCEED, handshaker {}", handshaker); | |
116 | - } else if (handshaker instanceof ResumingServerHandshaker) { | |
117 | - log.info("DTLS abbreviated Handshake initiated by server : SUCCEED, handshaker {}", handshaker); | |
118 | - } else if (handshaker instanceof ClientHandshaker) { | |
119 | - log.info("DTLS Full Handshake initiated by client : SUCCEED, handshaker {}", handshaker); | |
120 | - } else if (handshaker instanceof ResumingClientHandshaker) { | |
121 | - log.info("DTLS abbreviated Handshake initiated by client : SUCCEED, handshaker {}", handshaker); | |
122 | - } | |
123 | - } | |
124 | - | |
125 | - @Override | |
126 | - public void handshakeFailed(Handshaker handshaker, Throwable error) { | |
127 | - /** get cause */ | |
128 | - String cause; | |
129 | - if (error != null) { | |
130 | - if (error.getMessage() != null) { | |
131 | - cause = error.getMessage(); | |
132 | - } else { | |
133 | - cause = error.getClass().getName(); | |
134 | - } | |
135 | - } else { | |
136 | - cause = "unknown cause"; | |
137 | - } | |
138 | - | |
139 | - if (handshaker instanceof ServerHandshaker) { | |
140 | - log.info("DTLS Full Handshake initiated by server : FAILED [{}]", cause); | |
141 | - } else if (handshaker instanceof ResumingServerHandshaker) { | |
142 | - log.info("DTLS abbreviated Handshake initiated by server : FAILED [{}]", cause); | |
143 | - } else if (handshaker instanceof ClientHandshaker) { | |
144 | - log.info("DTLS Full Handshake initiated by client : FAILED [{}]", cause); | |
145 | - } else if (handshaker instanceof ResumingClientHandshaker) { | |
146 | - log.info("DTLS abbreviated Handshake initiated by client : FAILED [{}]", cause); | |
147 | - } | |
148 | - } | |
149 | - }); | |
150 | - } | |
151 | - }; | |
91 | + public CoapEndpoint createUnsecuredEndpoint(InetSocketAddress address, NetworkConfig coapConfig, | |
92 | + ObservationStore store) { | |
93 | + CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); | |
94 | + builder.setInetSocketAddress(address); | |
95 | + builder.setNetworkConfig(coapConfig); | |
96 | + return builder.build(); | |
97 | + } | |
98 | + | |
99 | + @Override | |
100 | + public CoapEndpoint createSecuredEndpoint(DtlsConnectorConfig dtlsConfig, NetworkConfig coapConfig, | |
101 | + ObservationStore store) { | |
102 | + CoapEndpoint.Builder builder = new CoapEndpoint.Builder(); | |
103 | + DtlsConnectorConfig.Builder dtlsConfigBuilder = new DtlsConnectorConfig.Builder(dtlsConfig); | |
104 | + | |
105 | + // tricks to be able to change psk information on the fly | |
106 | +// AdvancedPskStore pskStore = dtlsConfig.getAdvancedPskStore(); | |
107 | +// if (pskStore != null) { | |
108 | +// PskPublicInformation identity = pskStore.getIdentity(null, null); | |
109 | +// SecretKey key = pskStore | |
110 | +// .requestPskSecretResult(ConnectionId.EMPTY, null, identity, null, null, null).getSecret(); | |
111 | +// singlePSKStore = new SinglePSKStore(identity, key); | |
112 | +// dtlsConfigBuilder.setAdvancedPskStore(singlePSKStore); | |
113 | +// } | |
114 | + builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build())); | |
115 | + builder.setNetworkConfig(coapConfig); | |
116 | + return builder.build(); | |
152 | 117 | } |
153 | 118 | }; |
154 | 119 | |
120 | + | |
155 | 121 | LeshanClientBuilder builder = new LeshanClientBuilder(endpoint); |
156 | 122 | builder.setLocalAddress("0.0.0.0", 11000); |
157 | 123 | builder.setObjects(initializer.createAll()); |
... | ... | @@ -246,6 +212,11 @@ public class LwM2MTestClient { |
246 | 212 | public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) { |
247 | 213 | log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId()); |
248 | 214 | } |
215 | + | |
216 | + @Override | |
217 | + public void onUnexpectedError(Throwable unexpectedError) { | |
218 | + | |
219 | + } | |
249 | 220 | }; |
250 | 221 | this.client.addObserver(observer); |
251 | 222 | ... | ... |
... | ... | @@ -97,7 +97,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl |
97 | 97 | } |
98 | 98 | |
99 | 99 | @Override |
100 | - public WriteResponse write(ServerIdentity identity, int resourceid, LwM2mResource value) { | |
100 | + public WriteResponse write(ServerIdentity identity, boolean replace, int resourceid, LwM2mResource value) { | |
101 | 101 | log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid); |
102 | 102 | |
103 | 103 | switch (resourceid) { |
... | ... | @@ -112,7 +112,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl |
112 | 112 | fireResourcesChange(resourceid); |
113 | 113 | return WriteResponse.success(); |
114 | 114 | default: |
115 | - return super.write(identity, resourceid, value); | |
115 | + return super.write(identity, replace, resourceid, value); | |
116 | 116 | } |
117 | 117 | } |
118 | 118 | ... | ... |
No preview for this file type
No preview for this file type
... | ... | @@ -16,20 +16,31 @@ |
16 | 16 | package org.thingsboard.server.dao.oauth2; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
19 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
20 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; | |
19 | +import org.thingsboard.server.common.data.oauth2.OAuth2Info; | |
20 | +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; | |
21 | +import org.thingsboard.server.common.data.oauth2.PlatformType; | |
22 | +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; | |
23 | +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; | |
21 | 24 | |
22 | 25 | import java.util.List; |
23 | 26 | import java.util.UUID; |
24 | 27 | |
25 | 28 | public interface OAuth2Service { |
26 | - List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName); | |
29 | + List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType); | |
27 | 30 | |
31 | + @Deprecated | |
28 | 32 | void saveOAuth2Params(OAuth2ClientsParams oauth2Params); |
29 | 33 | |
34 | + @Deprecated | |
30 | 35 | OAuth2ClientsParams findOAuth2Params(); |
31 | 36 | |
32 | - OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id); | |
37 | + void saveOAuth2Info(OAuth2Info oauth2Info); | |
33 | 38 | |
34 | - List<OAuth2ClientRegistrationInfo> findAllClientRegistrationInfos(); | |
39 | + OAuth2Info findOAuth2Info(); | |
40 | + | |
41 | + OAuth2Registration findRegistration(UUID id); | |
42 | + | |
43 | + List<OAuth2Registration> findAllRegistrations(); | |
44 | + | |
45 | + String findCallbackUrlScheme(UUID registrationId, String pkgName); | |
35 | 46 | } | ... | ... |
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 | +package org.thingsboard.server.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public class OAuth2DomainId extends UUIDBased { | |
24 | + | |
25 | + @JsonCreator | |
26 | + public OAuth2DomainId(@JsonProperty("id") UUID id) { | |
27 | + super(id); | |
28 | + } | |
29 | + | |
30 | + public static OAuth2DomainId fromString(String oauth2DomainId) { | |
31 | + return new OAuth2DomainId(UUID.fromString(oauth2DomainId)); | |
32 | + } | |
33 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public class OAuth2MobileId extends UUIDBased { | |
24 | + | |
25 | + @JsonCreator | |
26 | + public OAuth2MobileId(@JsonProperty("id") UUID id) { | |
27 | + super(id); | |
28 | + } | |
29 | + | |
30 | + public static OAuth2MobileId fromString(String oauth2MobileId) { | |
31 | + return new OAuth2MobileId(UUID.fromString(oauth2MobileId)); | |
32 | + } | |
33 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public class OAuth2ParamsId extends UUIDBased { | |
24 | + | |
25 | + @JsonCreator | |
26 | + public OAuth2ParamsId(@JsonProperty("id") UUID id) { | |
27 | + super(id); | |
28 | + } | |
29 | + | |
30 | + public static OAuth2ParamsId fromString(String oauth2ParamsId) { | |
31 | + return new OAuth2ParamsId(UUID.fromString(oauth2ParamsId)); | |
32 | + } | |
33 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java
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 | +package org.thingsboard.server.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public class OAuth2RegistrationId extends UUIDBased { | |
24 | + | |
25 | + @JsonCreator | |
26 | + public OAuth2RegistrationId(@JsonProperty("id") UUID id) { | |
27 | + super(id); | |
28 | + } | |
29 | + | |
30 | + public static OAuth2RegistrationId fromString(String oauth2RegistrationId) { | |
31 | + return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId)); | |
32 | + } | |
33 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java
... | ... | @@ -13,13 +13,15 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.id; | |
16 | +package org.thingsboard.server.common.data.id.deprecated; | |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.annotation.JsonCreator; |
19 | 19 | import com.fasterxml.jackson.annotation.JsonProperty; |
20 | +import org.thingsboard.server.common.data.id.UUIDBased; | |
20 | 21 | |
21 | 22 | import java.util.UUID; |
22 | 23 | |
24 | +@Deprecated | |
23 | 25 | public class OAuth2ClientRegistrationId extends UUIDBased { |
24 | 26 | |
25 | 27 | @JsonCreator | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java
... | ... | @@ -13,13 +13,15 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.id; | |
16 | +package org.thingsboard.server.common.data.id.deprecated; | |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.annotation.JsonCreator; |
19 | 19 | import com.fasterxml.jackson.annotation.JsonProperty; |
20 | +import org.thingsboard.server.common.data.id.UUIDBased; | |
20 | 21 | |
21 | 22 | import java.util.UUID; |
22 | 23 | |
24 | +@Deprecated | |
23 | 25 | public class OAuth2ClientRegistrationInfoId extends UUIDBased { |
24 | 26 | |
25 | 27 | @JsonCreator | ... | ... |
... | ... | @@ -20,10 +20,8 @@ import lombok.EqualsAndHashCode; |
20 | 20 | import lombok.NoArgsConstructor; |
21 | 21 | import lombok.ToString; |
22 | 22 | import org.thingsboard.server.common.data.HasName; |
23 | -import org.thingsboard.server.common.data.HasTenantId; | |
24 | 23 | import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
25 | 24 | import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
26 | -import org.thingsboard.server.common.data.id.TenantId; | |
27 | 25 | |
28 | 26 | import java.util.List; |
29 | 27 | ... | ... |
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import lombok.ToString; | |
22 | +import org.thingsboard.server.common.data.BaseData; | |
23 | +import org.thingsboard.server.common.data.id.OAuth2DomainId; | |
24 | +import org.thingsboard.server.common.data.id.OAuth2ParamsId; | |
25 | + | |
26 | +@EqualsAndHashCode(callSuper = true) | |
27 | +@Data | |
28 | +@ToString | |
29 | +@NoArgsConstructor | |
30 | +public class OAuth2Domain extends BaseData<OAuth2DomainId> { | |
31 | + | |
32 | + private OAuth2ParamsId oauth2ParamsId; | |
33 | + private String domainName; | |
34 | + private SchemeType domainScheme; | |
35 | + | |
36 | + public OAuth2Domain(OAuth2Domain domain) { | |
37 | + super(domain); | |
38 | + this.oauth2ParamsId = domain.oauth2ParamsId; | |
39 | + this.domainName = domain.domainName; | |
40 | + this.domainScheme = domain.domainScheme; | |
41 | + } | |
42 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Builder; | |
20 | +import lombok.Data; | |
21 | +import lombok.EqualsAndHashCode; | |
22 | +import lombok.NoArgsConstructor; | |
23 | +import lombok.ToString; | |
24 | + | |
25 | +@EqualsAndHashCode | |
26 | +@Data | |
27 | +@ToString | |
28 | +@NoArgsConstructor | |
29 | +@AllArgsConstructor | |
30 | +@Builder | |
31 | +public class OAuth2DomainInfo { | |
32 | + private SchemeType scheme; | |
33 | + private String name; | |
34 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.*; | |
19 | + | |
20 | +import java.util.List; | |
21 | + | |
22 | +@EqualsAndHashCode | |
23 | +@Data | |
24 | +@ToString | |
25 | +@Builder(toBuilder = true) | |
26 | +@NoArgsConstructor | |
27 | +@AllArgsConstructor | |
28 | +public class OAuth2Info { | |
29 | + private boolean enabled; | |
30 | + private List<OAuth2ParamsInfo> oauth2ParamsInfos; | |
31 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import lombok.ToString; | |
22 | +import org.thingsboard.server.common.data.BaseData; | |
23 | +import org.thingsboard.server.common.data.id.OAuth2MobileId; | |
24 | +import org.thingsboard.server.common.data.id.OAuth2ParamsId; | |
25 | + | |
26 | +@EqualsAndHashCode(callSuper = true) | |
27 | +@Data | |
28 | +@ToString | |
29 | +@NoArgsConstructor | |
30 | +public class OAuth2Mobile extends BaseData<OAuth2MobileId> { | |
31 | + | |
32 | + private OAuth2ParamsId oauth2ParamsId; | |
33 | + private String pkgName; | |
34 | + private String callbackUrlScheme; | |
35 | + | |
36 | + public OAuth2Mobile(OAuth2Mobile mobile) { | |
37 | + super(mobile); | |
38 | + this.oauth2ParamsId = mobile.oauth2ParamsId; | |
39 | + this.pkgName = mobile.pkgName; | |
40 | + this.callbackUrlScheme = mobile.callbackUrlScheme; | |
41 | + } | |
42 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Builder; | |
20 | +import lombok.Data; | |
21 | +import lombok.EqualsAndHashCode; | |
22 | +import lombok.NoArgsConstructor; | |
23 | +import lombok.ToString; | |
24 | + | |
25 | +@EqualsAndHashCode | |
26 | +@Data | |
27 | +@ToString | |
28 | +@NoArgsConstructor | |
29 | +@AllArgsConstructor | |
30 | +@Builder | |
31 | +public class OAuth2MobileInfo { | |
32 | + private String pkgName; | |
33 | + private String callbackUrlScheme; | |
34 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import lombok.ToString; | |
22 | +import org.thingsboard.server.common.data.BaseData; | |
23 | +import org.thingsboard.server.common.data.id.OAuth2ParamsId; | |
24 | +import org.thingsboard.server.common.data.id.TenantId; | |
25 | + | |
26 | +@EqualsAndHashCode(callSuper = true) | |
27 | +@Data | |
28 | +@ToString | |
29 | +@NoArgsConstructor | |
30 | +public class OAuth2Params extends BaseData<OAuth2ParamsId> { | |
31 | + | |
32 | + private boolean enabled; | |
33 | + private TenantId tenantId; | |
34 | + | |
35 | + public OAuth2Params(OAuth2Params oauth2Params) { | |
36 | + super(oauth2Params); | |
37 | + this.enabled = oauth2Params.enabled; | |
38 | + this.tenantId = oauth2Params.tenantId; | |
39 | + } | |
40 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Builder; | |
20 | +import lombok.Data; | |
21 | +import lombok.EqualsAndHashCode; | |
22 | +import lombok.NoArgsConstructor; | |
23 | +import lombok.ToString; | |
24 | + | |
25 | +import java.util.List; | |
26 | + | |
27 | +@EqualsAndHashCode | |
28 | +@Data | |
29 | +@ToString | |
30 | +@Builder(toBuilder = true) | |
31 | +@NoArgsConstructor | |
32 | +@AllArgsConstructor | |
33 | +public class OAuth2ParamsInfo { | |
34 | + | |
35 | + private List<OAuth2DomainInfo> domainInfos; | |
36 | + private List<OAuth2MobileInfo> mobileInfos; | |
37 | + private List<OAuth2RegistrationInfo> clientRegistrations; | |
38 | + | |
39 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
19 | +import lombok.Data; | |
20 | +import lombok.EqualsAndHashCode; | |
21 | +import lombok.NoArgsConstructor; | |
22 | +import lombok.ToString; | |
23 | +import org.thingsboard.server.common.data.HasName; | |
24 | +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; | |
25 | +import org.thingsboard.server.common.data.id.OAuth2ParamsId; | |
26 | +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; | |
27 | + | |
28 | +import java.util.List; | |
29 | + | |
30 | +@EqualsAndHashCode(callSuper = true) | |
31 | +@Data | |
32 | +@ToString(exclude = {"clientSecret"}) | |
33 | +@NoArgsConstructor | |
34 | +public class OAuth2Registration extends SearchTextBasedWithAdditionalInfo<OAuth2RegistrationId> implements HasName { | |
35 | + | |
36 | + private OAuth2ParamsId oauth2ParamsId; | |
37 | + private OAuth2MapperConfig mapperConfig; | |
38 | + private String clientId; | |
39 | + private String clientSecret; | |
40 | + private String authorizationUri; | |
41 | + private String accessTokenUri; | |
42 | + private List<String> scope; | |
43 | + private String userInfoUri; | |
44 | + private String userNameAttributeName; | |
45 | + private String jwkSetUri; | |
46 | + private String clientAuthenticationMethod; | |
47 | + private String loginButtonLabel; | |
48 | + private String loginButtonIcon; | |
49 | + private List<PlatformType> platforms; | |
50 | + | |
51 | + public OAuth2Registration(OAuth2Registration registration) { | |
52 | + super(registration); | |
53 | + this.oauth2ParamsId = registration.oauth2ParamsId; | |
54 | + this.mapperConfig = registration.mapperConfig; | |
55 | + this.clientId = registration.clientId; | |
56 | + this.clientSecret = registration.clientSecret; | |
57 | + this.authorizationUri = registration.authorizationUri; | |
58 | + this.accessTokenUri = registration.accessTokenUri; | |
59 | + this.scope = registration.scope; | |
60 | + this.userInfoUri = registration.userInfoUri; | |
61 | + this.userNameAttributeName = registration.userNameAttributeName; | |
62 | + this.jwkSetUri = registration.jwkSetUri; | |
63 | + this.clientAuthenticationMethod = registration.clientAuthenticationMethod; | |
64 | + this.loginButtonLabel = registration.loginButtonLabel; | |
65 | + this.loginButtonIcon = registration.loginButtonIcon; | |
66 | + this.platforms = registration.platforms; | |
67 | + } | |
68 | + | |
69 | + @Override | |
70 | + @JsonProperty(access = JsonProperty.Access.READ_ONLY) | |
71 | + public String getName() { | |
72 | + return loginButtonLabel; | |
73 | + } | |
74 | + | |
75 | + @Override | |
76 | + public String getSearchText() { | |
77 | + return getName(); | |
78 | + } | |
79 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java
... | ... | @@ -17,7 +17,6 @@ package org.thingsboard.server.common.data.oauth2; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | 19 | import lombok.*; |
20 | -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
21 | 20 | |
22 | 21 | import java.util.List; |
23 | 22 | |
... | ... | @@ -27,7 +26,7 @@ import java.util.List; |
27 | 26 | @NoArgsConstructor |
28 | 27 | @AllArgsConstructor |
29 | 28 | @Builder |
30 | -public class ClientRegistrationDto { | |
29 | +public class OAuth2RegistrationInfo { | |
31 | 30 | private OAuth2MapperConfig mapperConfig; |
32 | 31 | private String clientId; |
33 | 32 | private String clientSecret; |
... | ... | @@ -40,5 +39,6 @@ public class ClientRegistrationDto { |
40 | 39 | private String clientAuthenticationMethod; |
41 | 40 | private String loginButtonLabel; |
42 | 41 | private String loginButtonIcon; |
42 | + private List<PlatformType> platforms; | |
43 | 43 | private JsonNode additionalInfo; |
44 | 44 | } | ... | ... |
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 | +package org.thingsboard.server.common.data.oauth2; | |
17 | + | |
18 | +public enum PlatformType { | |
19 | + WEB, ANDROID, IOS | |
20 | +} | ... | ... |
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 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import lombok.*; | |
20 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | |
21 | + | |
22 | +import java.util.List; | |
23 | + | |
24 | +@Deprecated | |
25 | +@EqualsAndHashCode | |
26 | +@Data | |
27 | +@ToString(exclude = {"clientSecret"}) | |
28 | +@NoArgsConstructor | |
29 | +@AllArgsConstructor | |
30 | +@Builder | |
31 | +public class ClientRegistrationDto { | |
32 | + private OAuth2MapperConfig mapperConfig; | |
33 | + private String clientId; | |
34 | + private String clientSecret; | |
35 | + private String authorizationUri; | |
36 | + private String accessTokenUri; | |
37 | + private List<String> scope; | |
38 | + private String userInfoUri; | |
39 | + private String userNameAttributeName; | |
40 | + private String jwkSetUri; | |
41 | + private String clientAuthenticationMethod; | |
42 | + private String loginButtonLabel; | |
43 | + private String loginButtonIcon; | |
44 | + private JsonNode additionalInfo; | |
45 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java
... | ... | @@ -13,10 +13,12 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | 17 | |
18 | 18 | import lombok.*; |
19 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
19 | 20 | |
21 | +@Deprecated | |
20 | 22 | @EqualsAndHashCode |
21 | 23 | @Data |
22 | 24 | @ToString | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java
... | ... | @@ -13,11 +13,14 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.EqualsAndHashCode; |
20 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
21 | +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; | |
20 | 22 | |
23 | +@Deprecated | |
21 | 24 | @EqualsAndHashCode(callSuper = true) |
22 | 25 | @Data |
23 | 26 | public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo { | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java
... | ... | @@ -13,16 +13,18 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.EqualsAndHashCode; |
20 | 20 | import lombok.NoArgsConstructor; |
21 | 21 | import lombok.ToString; |
22 | 22 | import org.thingsboard.server.common.data.BaseData; |
23 | -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; | |
24 | -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
23 | +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId; | |
24 | +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; | |
25 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
25 | 26 | |
27 | +@Deprecated | |
26 | 28 | @EqualsAndHashCode(callSuper = true) |
27 | 29 | @Data |
28 | 30 | @ToString | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.annotation.JsonProperty; |
19 | 19 | import lombok.Data; |
... | ... | @@ -22,10 +22,12 @@ import lombok.NoArgsConstructor; |
22 | 22 | import lombok.ToString; |
23 | 23 | import org.thingsboard.server.common.data.HasName; |
24 | 24 | import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
25 | -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
25 | +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; | |
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | |
26 | 27 | |
27 | 28 | import java.util.List; |
28 | 29 | |
30 | +@Deprecated | |
29 | 31 | @EqualsAndHashCode(callSuper = true) |
30 | 32 | @Data |
31 | 33 | @ToString(exclude = {"clientSecret"}) | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java
... | ... | @@ -13,13 +13,13 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | 17 | |
18 | 18 | import lombok.*; |
19 | 19 | |
20 | 20 | import java.util.List; |
21 | -import java.util.Set; | |
22 | 21 | |
22 | +@Deprecated | |
23 | 23 | @EqualsAndHashCode |
24 | 24 | @Data |
25 | 25 | @ToString | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java
... | ... | @@ -13,13 +13,13 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.data.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2.deprecated; | |
17 | 17 | |
18 | 18 | import lombok.*; |
19 | 19 | |
20 | 20 | import java.util.List; |
21 | -import java.util.Set; | |
22 | 21 | |
22 | +@Deprecated | |
23 | 23 | @EqualsAndHashCode |
24 | 24 | @Data |
25 | 25 | @ToString | ... | ... |
... | ... | @@ -310,10 +310,12 @@ message SessionCloseNotificationProto { |
310 | 310 | |
311 | 311 | message SubscribeToAttributeUpdatesMsg { |
312 | 312 | bool unsubscribe = 1; |
313 | + SessionType sessionType = 2; | |
313 | 314 | } |
314 | 315 | |
315 | 316 | message SubscribeToRPCMsg { |
316 | 317 | bool unsubscribe = 1; |
318 | + SessionType sessionType = 2; | |
317 | 319 | } |
318 | 320 | |
319 | 321 | message ToDeviceRpcRequestMsg { | ... | ... |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
... | ... | @@ -483,13 +483,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
483 | 483 | String title = exchange.getQueryParameter("title"); |
484 | 484 | String version = exchange.getQueryParameter("version"); |
485 | 485 | if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { |
486 | + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); | |
486 | 487 | if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { |
487 | - String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); | |
488 | 488 | String strChunkSize = exchange.getQueryParameter("size"); |
489 | 489 | String strChunk = exchange.getQueryParameter("chunk"); |
490 | 490 | int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); |
491 | 491 | int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); |
492 | 492 | exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); |
493 | + } | |
494 | + else if (firmwareId != null) { | |
495 | + sendOtaData(exchange, firmwareId); | |
493 | 496 | } else { |
494 | 497 | exchange.respond(CoAP.ResponseCode.BAD_REQUEST); |
495 | 498 | } |
... | ... | @@ -505,6 +508,20 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
505 | 508 | } |
506 | 509 | } |
507 | 510 | |
511 | + private void sendOtaData(CoapExchange exchange, String firmwareId) { | |
512 | + Response response = new Response(CoAP.ResponseCode.CONTENT); | |
513 | + byte[] fwData = transportContext.getOtaPackageDataCache().get(firmwareId); | |
514 | + if (fwData != null && fwData.length > 0) { | |
515 | + response.setPayload(fwData); | |
516 | + if (exchange.getRequestOptions().getBlock2() != null) { | |
517 | + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); | |
518 | + boolean moreFlag = fwData.length > chunkSize; | |
519 | + response.getOptions().setBlock2(chunkSize, moreFlag, 0); | |
520 | + } | |
521 | + exchange.respond(response); | |
522 | + } | |
523 | + } | |
524 | + | |
508 | 525 | private static class CoapSessionListener implements SessionMsgListener { |
509 | 526 | |
510 | 527 | private final CoapTransportResource coapTransportResource; |
... | ... | @@ -572,7 +589,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
572 | 589 | } |
573 | 590 | if (msg.getPersisted()) { |
574 | 591 | RpcStatus status; |
575 | - if (successful) { | |
592 | + if (!successful) { | |
576 | 593 | status = RpcStatus.FAILED; |
577 | 594 | } else if (msg.getOneway()) { |
578 | 595 | status = RpcStatus.SUCCESSFUL; | ... | ... |
... | ... | @@ -102,15 +102,15 @@ public class LwM2MTransportBootstrapService { |
102 | 102 | builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort()); |
103 | 103 | |
104 | 104 | /** Create CoAP Config */ |
105 | - builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort())); | |
105 | + builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig)); | |
106 | 106 | |
107 | 107 | /** Define model provider (Create Models )*/ |
108 | 108 | |
109 | 109 | /** Create credentials */ |
110 | 110 | this.setServerWithCredentials(builder); |
111 | 111 | |
112 | - /** Set securityStore with new ConfigStore */ | |
113 | - builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); | |
112 | +// /** Set securityStore with new ConfigStore */ | |
113 | +// builder.setConfigStore(lwM2MInMemoryBootstrapConfigStore); | |
114 | 114 | |
115 | 115 | /** SecurityStore */ |
116 | 116 | builder.setSecurityStore(lwM2MBootstrapSecurityStore); | ... | ... |
... | ... | @@ -69,7 +69,7 @@ public class LwM2MBootstrapConfig { |
69 | 69 | server0.lifetime = servers.getLifetime(); |
70 | 70 | server0.defaultMinPeriod = servers.getDefaultMinPeriod(); |
71 | 71 | server0.notifIfDisabled = servers.isNotifIfDisabled(); |
72 | - server0.binding = BindingMode.valueOf(servers.getBinding()); | |
72 | + server0.binding = BindingMode.parse(servers.getBinding()); | |
73 | 73 | configBs.servers.put(0, server0); |
74 | 74 | /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */ |
75 | 75 | this.bootstrapServer.setBootstrapServerIs(true); | ... | ... |
... | ... | @@ -30,7 +30,7 @@ import org.eclipse.leshan.server.security.SecurityInfo; |
30 | 30 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
31 | 31 | import org.springframework.stereotype.Service; |
32 | 32 | import org.thingsboard.server.gen.transport.TransportProtos; |
33 | -import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo; | |
33 | +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; | |
34 | 34 | import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; |
35 | 35 | import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; |
36 | 36 | import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; |
... | ... | @@ -40,7 +40,7 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; |
40 | 40 | import java.io.IOException; |
41 | 41 | import java.security.GeneralSecurityException; |
42 | 42 | import java.util.Collections; |
43 | -import java.util.List; | |
43 | +import java.util.Iterator; | |
44 | 44 | import java.util.UUID; |
45 | 45 | |
46 | 46 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER; |
... | ... | @@ -71,8 +71,8 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { |
71 | 71 | } |
72 | 72 | |
73 | 73 | @Override |
74 | - public List<SecurityInfo> getAllByEndpoint(String endPoint) { | |
75 | - EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); | |
74 | + public Iterator<SecurityInfo> getAllByEndpoint(String endPoint) { | |
75 | + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); | |
76 | 76 | if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { |
77 | 77 | /* add value to store from BootstrapJson */ |
78 | 78 | this.setBootstrapConfigScurityInfo(store); |
... | ... | @@ -88,7 +88,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { |
88 | 88 | } catch (InvalidConfigurationException e) { |
89 | 89 | log.error("", e); |
90 | 90 | } |
91 | - return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()); | |
91 | + return store.getSecurityInfo() == null ? null : Collections.singletonList(store.getSecurityInfo()).iterator(); | |
92 | 92 | } |
93 | 93 | } |
94 | 94 | return null; |
... | ... | @@ -96,7 +96,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { |
96 | 96 | |
97 | 97 | @Override |
98 | 98 | public SecurityInfo getByIdentity(String identity) { |
99 | - EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); | |
99 | + TbLwM2MSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfoByCredentialsId(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP); | |
100 | 100 | if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) { |
101 | 101 | /* add value to store from BootstrapJson */ |
102 | 102 | this.setBootstrapConfigScurityInfo(store); |
... | ... | @@ -113,7 +113,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { |
113 | 113 | return null; |
114 | 114 | } |
115 | 115 | |
116 | - private void setBootstrapConfigScurityInfo(EndpointSecurityInfo store) { | |
116 | + private void setBootstrapConfigScurityInfo(TbLwM2MSecurityInfo store) { | |
117 | 117 | /* BootstrapConfig */ |
118 | 118 | LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store); |
119 | 119 | if (lwM2MBootstrapConfig != null) { |
... | ... | @@ -150,7 +150,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { |
150 | 150 | } |
151 | 151 | } |
152 | 152 | |
153 | - private LwM2MBootstrapConfig getParametersBootstrap(EndpointSecurityInfo store) { | |
153 | + private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) { | |
154 | 154 | try { |
155 | 155 | LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); |
156 | 156 | if (lwM2MBootstrapConfig != null) { | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.transport.lwm2m.bootstrap.secure; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.eclipse.leshan.core.request.BootstrapRequest; | |
19 | 20 | import org.eclipse.leshan.core.request.Identity; |
20 | 21 | import org.eclipse.leshan.server.bootstrap.BootstrapSession; |
21 | 22 | import org.eclipse.leshan.server.bootstrap.DefaultBootstrapSession; |
... | ... | @@ -25,7 +26,7 @@ import org.eclipse.leshan.server.security.SecurityChecker; |
25 | 26 | import org.eclipse.leshan.server.security.SecurityInfo; |
26 | 27 | |
27 | 28 | import java.util.Collections; |
28 | -import java.util.List; | |
29 | +import java.util.Iterator; | |
29 | 30 | |
30 | 31 | @Slf4j |
31 | 32 | public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSessionManager { |
... | ... | @@ -50,16 +51,17 @@ public class LwM2mDefaultBootstrapSessionManager extends DefaultBootstrapSession |
50 | 51 | } |
51 | 52 | |
52 | 53 | @SuppressWarnings("deprecation") |
53 | - public BootstrapSession begin(String endpoint, Identity clientIdentity) { | |
54 | + public BootstrapSession begin(BootstrapRequest request, Identity clientIdentity) { | |
54 | 55 | boolean authorized; |
55 | 56 | if (bsSecurityStore != null) { |
56 | - List<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())) : bsSecurityStore.getAllByEndpoint(endpoint); | |
57 | + Iterator<SecurityInfo> securityInfos = (clientIdentity.getPskIdentity() != null && !clientIdentity.getPskIdentity().isEmpty()) ? | |
58 | + Collections.singletonList(bsSecurityStore.getByIdentity(clientIdentity.getPskIdentity())).iterator() : bsSecurityStore.getAllByEndpoint(request.getEndpointName()); | |
57 | 59 | log.info("Bootstrap session started securityInfos: [{}]", securityInfos); |
58 | - authorized = securityChecker.checkSecurityInfos(endpoint, clientIdentity, securityInfos); | |
60 | + authorized = securityChecker.checkSecurityInfos(request.getEndpointName(), clientIdentity, securityInfos); | |
59 | 61 | } else { |
60 | 62 | authorized = true; |
61 | 63 | } |
62 | - DefaultBootstrapSession session = new DefaultBootstrapSession(endpoint, clientIdentity, authorized); | |
64 | + DefaultBootstrapSession session = new DefaultBootstrapSession(request, clientIdentity, authorized); | |
63 | 65 | log.info("Bootstrap session started : {}", session); |
64 | 66 | return session; |
65 | 67 | } | ... | ... |