Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
		
						commit
						e39e37babf
					
				@ -955,7 +955,7 @@
 | 
			
		||||
            },
 | 
			
		||||
            "methodName": "gateway_restart",
 | 
			
		||||
            "methodParams": "{}",
 | 
			
		||||
            "buttonText": "gateway restart"
 | 
			
		||||
            "buttonText": "GATEWAY RESTART"
 | 
			
		||||
          },
 | 
			
		||||
          "title": "New RPC Button",
 | 
			
		||||
          "dropShadow": true,
 | 
			
		||||
 | 
			
		||||
@ -147,9 +147,9 @@
 | 
			
		||||
                "name": "Add",
 | 
			
		||||
                "icon": "add",
 | 
			
		||||
                "type": "customPretty",
 | 
			
		||||
                "customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n        (ngSubmit)=\"save()\" class=\"add-entity-form\">\n    <mat-toolbar color=\"primary\">\n        <h2>Add thermostat</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 mat-dialog-content fxLayout=\"column\">\n        <mat-form-field fxFlex class=\"mat-block\">\n            <mat-label>Thermostat name</mat-label>\n            <input matInput formControlName=\"entityName\" required>\n            <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n                Thermostat name is required.\n            </mat-error>\n        </mat-form-field>\n        <div formGroupName=\"attributes\" fxLayout=\"column\">\n            <mat-slide-toggle formControlName=\"alarmTemperature\">\n                High temperature alarm\n            </mat-slide-toggle>\n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>High temperature threshold, °C</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                    [required] = \"addEntityFormGroup.get('attributes').get('alarmTemperature').value\"\n                    formControlName=\"thresholdTemperature\">\n                <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('thresholdTemperature').hasError('required')\">\n                    High temperature threshold is required.\n                </mat-error>\n            </mat-form-field>\n        \n            <mat-slide-toggle formControlName=\"alarmHumidity\">\n                Low humidity alarm\n            </mat-slide-toggle>\n                \n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>Low humidity threshold, %</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                    [required] = \"addEntityFormGroup.get('attributes').get('alarmHumidity').value\"\n                    formControlName=\"thresholdHumidity\">\n                <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('thresholdHumidity').hasError('required')\">\n                    Low humidity threshold is required.\n                </mat-error>\n            </mat-form-field>\n        </div>\n    </div>\n    <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n        <button mat-button mat-raised-button color=\"primary\"\n                type=\"submit\"\n                [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n            Create\n        </button>\n        <button mat-button color=\"primary\"\n                type=\"button\"\n                [disabled]=\"(isLoading$ | async)\"\n                (click)=\"cancel()\" cdkFocusInitial>\n            Cancel\n        </button>\n    </div>\n</form>",
 | 
			
		||||
                "customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n        (ngSubmit)=\"save()\" class=\"add-entity-form\">\n    <mat-toolbar color=\"primary\">\n        <h2>Add thermostat</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 mat-dialog-content fxLayout=\"column\">\n        <mat-form-field fxFlex class=\"mat-block\">\n            <mat-label>Thermostat name</mat-label>\n            <input matInput formControlName=\"entityName\" required>\n            <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n                Thermostat name is required.\n            </mat-error>\n        </mat-form-field>\n        <div formGroupName=\"attributes\" fxLayout=\"column\">\n            <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n                High temperature alarm\n            </mat-slide-toggle>\n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>High temperature threshold, °C</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                    [required] = \"addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n                    formControlName=\"temperatureAlarmThreshold\">\n                <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n                    High temperature threshold is required.\n                </mat-error>\n            </mat-form-field>\n        \n            <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n                Low humidity alarm\n            </mat-slide-toggle>\n                \n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>Low humidity threshold, %</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                    [required] = \"addEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n                    formControlName=\"humidityAlarmThreshold\">\n                <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n                    Low humidity threshold is required.\n                </mat-error>\n            </mat-form-field>\n        </div>\n    </div>\n    <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n        <button mat-button mat-raised-button color=\"primary\"\n                type=\"submit\"\n                [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n            Create\n        </button>\n        <button mat-button color=\"primary\"\n                type=\"button\"\n                [disabled]=\"(isLoading$ | async)\"\n                (click)=\"cancel()\" cdkFocusInitial>\n            Cancel\n        </button>\n    </div>\n</form>",
 | 
			
		||||
                "customCss": ".add-entity-form{\n    width: 300px;\n}\n",
 | 
			
		||||
                "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n    customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n    let vm = instance;\n    \n    vm.addEntityFormGroup = vm.fb.group({\n        entityName: ['', [vm.validators.required]],\n        attributes: vm.fb.group({\n            alarmTemperature: [false],\n            thresholdTemperature: [{value: null, disabled: true}],\n            alarmHumidity: [false],\n            thresholdHumidity: [{value: null, disabled: true}]\n        })\n    });\n    \n    vm.addEntityFormGroup.get('attributes').get('alarmTemperature').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.addEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n        } else {\n            vm.addEntityFormGroup.get('attributes').get('thresholdTemperature').disable();\n        }\n    });\n    \n    vm.addEntityFormGroup.get('attributes').get('alarmHumidity').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.addEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n        } else {\n            vm.addEntityFormGroup.get('attributes').get('thresholdHumidity').disable();\n        }\n    });\n\n    vm.save = function() {\n        vm.addEntityFormGroup.markAsPristine();\n        saveEntityObservable().subscribe(\n            function (entity) {\n                saveAttributes(entity.id).subscribe(\n                    function () {\n                        widgetContext.updateAliases();\n                        vm.dialogRef.close(null);\n                    }\n                );\n            }\n        );\n    };\n    \n    vm.cancel = function() {\n        vm.dialogRef.close(null);\n    };\n    \n    function saveEntityObservable() {\n        const formValues = vm.addEntityFormGroup.value;\n        let entity = {\n            name: formValues.entityName,\n            type: \"thermostat\"\n        };\n        return deviceService.saveDevice(entity);\n    }\n    \n    function saveAttributes(entityId) {\n        let attributes = vm.addEntityFormGroup.get('attributes').value;\n        let attributesArray = [];\n        for (let key in attributes) {\n            if(attributes[key] !== null) {\n                attributesArray.push({key: key, value: attributes[key]});\n            }\n        }\n        if (attributesArray.length > 0) {\n            return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n        } else {\n            return widgetContext.rxjs.of([]);\n        }\n    }\n}",
 | 
			
		||||
                "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n    customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n    let vm = instance;\n    \n    vm.addEntityFormGroup = vm.fb.group({\n        entityName: ['', [vm.validators.required]],\n        attributes: vm.fb.group({\n            temperatureAlarmFlag: [false],\n            temperatureAlarmThreshold: [{value: null, disabled: true}],\n            humidityAlarmFlag: [false],\n            humidityAlarmThreshold: [{value: null, disabled: true}]\n        })\n    });\n    \n    vm.addEntityFormGroup.get('attributes').get('temperatureAlarmFlag').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n        } else {\n            vm.addEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').disable();\n        }\n    });\n    \n    vm.addEntityFormGroup.get('attributes').get('humidityAlarmFlag').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n        } else {\n            vm.addEntityFormGroup.get('attributes').get('humidityAlarmThreshold').disable();\n        }\n    });\n\n    vm.save = function() {\n        vm.addEntityFormGroup.markAsPristine();\n        saveEntityObservable().subscribe(\n            function (entity) {\n                saveAttributes(entity.id).subscribe(\n                    function () {\n                        widgetContext.updateAliases();\n                        vm.dialogRef.close(null);\n                    }\n                );\n            }\n        );\n    };\n    \n    vm.cancel = function() {\n        vm.dialogRef.close(null);\n    };\n    \n    function saveEntityObservable() {\n        const formValues = vm.addEntityFormGroup.value;\n        let entity = {\n            name: formValues.entityName,\n            type: \"thermostat\"\n        };\n        return deviceService.saveDevice(entity);\n    }\n    \n    function saveAttributes(entityId) {\n        let attributes = vm.addEntityFormGroup.get('attributes').value;\n        let attributesArray = [];\n        for (let key in attributes) {\n            if(attributes[key] !== null) {\n                attributesArray.push({key: key, value: attributes[key]});\n            }\n        }\n        if (attributesArray.length > 0) {\n            return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n        } else {\n            return widgetContext.rxjs.of([]);\n        }\n    }\n}",
 | 
			
		||||
                "customResources": [],
 | 
			
		||||
                "id": "8ab5a518-67d2-b6a2-956d-81fd512294b2"
 | 
			
		||||
              }
 | 
			
		||||
@ -167,9 +167,9 @@
 | 
			
		||||
                "name": "Edit",
 | 
			
		||||
                "icon": "edit",
 | 
			
		||||
                "type": "customPretty",
 | 
			
		||||
                "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n      (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n    <mat-toolbar color=\"primary\">\n        <h2>Edit thermostat {{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 mat-dialog-content fxLayout=\"column\">\n        <mat-form-field fxFlex class=\"mat-block\">\n            <mat-label>Thermostat name</mat-label>\n            <input matInput formControlName=\"entityName\" readonly>\n        </mat-form-field>\n        <div formGroupName=\"attributes\" fxLayout=\"column\">\n            <mat-slide-toggle formControlName=\"alarmTemperature\">\n                High temperature alarm\n            </mat-slide-toggle>\n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>High temperature threshold, °C</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                        [required] = \"editEntityFormGroup.get('attributes').get('alarmTemperature').value\"\n                        formControlName=\"thresholdTemperature\">\n                <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('thresholdTemperature').hasError('required')\">\n                    High temperature threshold is required.\n                </mat-error>\n            </mat-form-field>\n\n            <mat-slide-toggle formControlName=\"alarmHumidity\">\n                Low humidity alarm\n            </mat-slide-toggle>\n\n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>Low humidity threshold, %</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                       [required] = \"editEntityFormGroup.get('attributes').get('alarmHumidity').value\"\n                       formControlName=\"thresholdHumidity\">\n                <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('thresholdHumidity').hasError('required')\">\n                    Low humidity threshold is required.\n                </mat-error>\n            </mat-form-field>\n        </div>\n    </div>\n    <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n        <button mat-raised-button color=\"primary\"\n                type=\"submit\"\n                [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n            Save\n        </button>\n        <button mat-button color=\"primary\"\n                type=\"button\"\n                [disabled]=\"(isLoading$ | async)\"\n                (click)=\"cancel()\" cdkFocusInitial>\n            Cancel\n        </button>\n    </div>\n</form>",
 | 
			
		||||
                "customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n      (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n    <mat-toolbar color=\"primary\">\n        <h2>Edit thermostat {{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 mat-dialog-content fxLayout=\"column\">\n        <mat-form-field fxFlex class=\"mat-block\">\n            <mat-label>Thermostat name</mat-label>\n            <input matInput formControlName=\"entityName\" readonly>\n        </mat-form-field>\n        <div formGroupName=\"attributes\" fxLayout=\"column\">\n            <mat-slide-toggle formControlName=\"temperatureAlarmFlag\">\n                High temperature alarm\n            </mat-slide-toggle>\n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>High temperature threshold, °C</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                        [required] = \"editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').value\"\n                        formControlName=\"temperatureAlarmThreshold\">\n                <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').hasError('required')\">\n                    High temperature threshold is required.\n                </mat-error>\n            </mat-form-field>\n\n            <mat-slide-toggle formControlName=\"humidityAlarmFlag\">\n                Low humidity alarm\n            </mat-slide-toggle>\n\n            <mat-form-field fxFlex class=\"mat-block\">\n                <mat-label>Low humidity threshold, %</mat-label>\n                <input type=\"number\" step=\"any\" matInput\n                       [required] = \"editEntityFormGroup.get('attributes').get('humidityAlarmFlag').value\"\n                       formControlName=\"humidityAlarmThreshold\">\n                <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').hasError('required')\">\n                    Low humidity threshold is required.\n                </mat-error>\n            </mat-form-field>\n        </div>\n    </div>\n    <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n        <button mat-raised-button color=\"primary\"\n                type=\"submit\"\n                [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n            Save\n        </button>\n        <button mat-button color=\"primary\"\n                type=\"button\"\n                [disabled]=\"(isLoading$ | async)\"\n                (click)=\"cancel()\" cdkFocusInitial>\n            Cancel\n        </button>\n    </div>\n</form>",
 | 
			
		||||
                "customCss": ".edit-entity-form{\n    width: 300px;\n}",
 | 
			
		||||
                "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n    customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n    let vm = instance;\n    \n    vm.entityId = entityId;\n    vm.entityName = entityName;\n    vm.attributes = {};\n    \n    vm.editEntityFormGroup = vm.fb.group({\n        entityName: [''],\n        attributes: vm.fb.group({\n            alarmTemperature: [false],\n            thresholdTemperature: [{value: null, disabled: true}],\n            alarmHumidity: [false],\n            thresholdHumidity: [{value: null, disabled: true}]\n        })\n    });\n    \n    vm.editEntityFormGroup.get('attributes').get('alarmTemperature').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n        } else {\n            vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').disable();\n        }\n    });\n    \n    vm.editEntityFormGroup.get('attributes').get('alarmHumidity').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n        } else {\n            vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').disable();\n        }\n    });\n    \n    \n    getEntityInfo();\n    \n    \n    vm.save = function() {\n        vm.editEntityFormGroup.markAsPristine();\n        saveAttributes(entityId).subscribe(\n            function () {\n                vm.dialogRef.close(null);\n            }\n        );\n    };\n    \n    vm.cancel = function() {\n        vm.dialogRef.close(null);\n    };\n    \n    function getEntityAttributes(attributes) {\n        for (var i = 0; i < attributes.length; i++) {\n            vm.attributes[attributes[i].key] = attributes[i].value;\n        }\n    }\n    \n    function getEntityInfo() {\n        attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE').subscribe(\n            function (attributes) {\n                getEntityAttributes(attributes);\n                vm.editEntityFormGroup.patchValue({\n                    entityName: vm.entityName,\n                    attributes: vm.attributes\n                });\n                // if(vm.attributes.alarmTemperature) {\n                //     vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n                // }\n                // if(vm.attributes.alarmHumidity) {\n                //     vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n                // }\n            }\n        );\n    }\n    \n    function saveAttributes(entityId) {\n        let attributes = vm.editEntityFormGroup.get('attributes').value;\n        let attributesArray = [];\n        for (let key in attributes) {\n            if (attributes[key] !== vm.attributes[key]) {\n                attributesArray.push({key: key, value: attributes[key]});\n            }\n        }\n        if (attributesArray.length > 0) {\n            return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n        } else {\n            return widgetContext.rxjs.of([]);\n        }\n    }\n}",
 | 
			
		||||
                "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n    customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n    let vm = instance;\n    \n    vm.entityId = entityId;\n    vm.entityName = entityName;\n    vm.attributes = {};\n    \n    vm.editEntityFormGroup = vm.fb.group({\n        entityName: [''],\n        attributes: vm.fb.group({\n            temperatureAlarmFlag: [false],\n            temperatureAlarmThreshold: [{value: null, disabled: true}],\n            humidityAlarmFlag: [false],\n            humidityAlarmThreshold: [{value: null, disabled: true}]\n        })\n    });\n    \n    vm.editEntityFormGroup.get('attributes').get('temperatureAlarmFlag').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n        } else {\n            vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').disable();\n        }\n    });\n    \n    vm.editEntityFormGroup.get('attributes').get('humidityAlarmFlag').valueChanges\n    .subscribe(activate => {\n        if (activate) {\n            vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n        } else {\n            vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').disable();\n        }\n    });\n    \n    \n    getEntityInfo();\n    \n    \n    vm.save = function() {\n        vm.editEntityFormGroup.markAsPristine();\n        saveAttributes(entityId).subscribe(\n            function () {\n                vm.dialogRef.close(null);\n            }\n        );\n    };\n    \n    vm.cancel = function() {\n        vm.dialogRef.close(null);\n    };\n    \n    function getEntityAttributes(attributes) {\n        for (var i = 0; i < attributes.length; i++) {\n            vm.attributes[attributes[i].key] = attributes[i].value;\n        }\n    }\n    \n    function getEntityInfo() {\n        attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE').subscribe(\n            function (attributes) {\n                getEntityAttributes(attributes);\n                vm.editEntityFormGroup.patchValue({\n                    entityName: vm.entityName,\n                    attributes: vm.attributes\n                });\n                // if(vm.attributes.temperatureAlarmFlag) {\n                //     vm.editEntityFormGroup.get('attributes').get('temperatureAlarmThreshold').enable();\n                // }\n                // if(vm.attributes.humidityAlarmFlag) {\n                //     vm.editEntityFormGroup.get('attributes').get('humidityAlarmThreshold').enable();\n                // }\n            }\n        );\n    }\n    \n    function saveAttributes(entityId) {\n        let attributes = vm.editEntityFormGroup.get('attributes').value;\n        let attributesArray = [];\n        for (let key in attributes) {\n            if (attributes[key] !== vm.attributes[key]) {\n                attributesArray.push({key: key, value: attributes[key]});\n            }\n        }\n        if (attributesArray.length > 0) {\n            return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n        } else {\n            return widgetContext.rxjs.of([]);\n        }\n    }\n}",
 | 
			
		||||
                "customResources": [],
 | 
			
		||||
                "id": "7506576f-87ba-d3a0-88fb-e304d451776d"
 | 
			
		||||
              },
 | 
			
		||||
@ -550,7 +550,7 @@
 | 
			
		||||
              "type": "entity",
 | 
			
		||||
              "dataKeys": [
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "alarmTemperature",
 | 
			
		||||
                  "name": "temperatureAlarmFlag",
 | 
			
		||||
                  "type": "attribute",
 | 
			
		||||
                  "label": "High temperature alarm",
 | 
			
		||||
                  "color": "#4caf50",
 | 
			
		||||
@ -565,7 +565,7 @@
 | 
			
		||||
                  "_hash": 0.8725278440159361
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "thresholdTemperature",
 | 
			
		||||
                  "name": "temperatureAlarmThreshold",
 | 
			
		||||
                  "type": "attribute",
 | 
			
		||||
                  "label": "High temperature threshold, °C",
 | 
			
		||||
                  "color": "#f44336",
 | 
			
		||||
@ -576,12 +576,12 @@
 | 
			
		||||
                    "isEditable": "editable",
 | 
			
		||||
                    "dataKeyHidden": false,
 | 
			
		||||
                    "step": 1,
 | 
			
		||||
                    "disabledOnDataKey": "alarmTemperature"
 | 
			
		||||
                    "disabledOnDataKey": "temperatureAlarmFlag"
 | 
			
		||||
                  },
 | 
			
		||||
                  "_hash": 0.7316078472857874
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "alarmHumidity",
 | 
			
		||||
                  "name": "humidityAlarmFlag",
 | 
			
		||||
                  "type": "attribute",
 | 
			
		||||
                  "label": "Low humidity alarm",
 | 
			
		||||
                  "color": "#ffc107",
 | 
			
		||||
@ -596,7 +596,7 @@
 | 
			
		||||
                  "_hash": 0.5339673667431057
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                  "name": "thresholdHumidity",
 | 
			
		||||
                  "name": "humidityAlarmThreshold",
 | 
			
		||||
                  "type": "attribute",
 | 
			
		||||
                  "label": "Low humidity threshold, %",
 | 
			
		||||
                  "color": "#607d8b",
 | 
			
		||||
@ -607,7 +607,7 @@
 | 
			
		||||
                    "isEditable": "editable",
 | 
			
		||||
                    "dataKeyHidden": false,
 | 
			
		||||
                    "step": 1,
 | 
			
		||||
                    "disabledOnDataKey": "alarmHumidity"
 | 
			
		||||
                    "disabledOnDataKey": "humidityAlarmFlag"
 | 
			
		||||
                  },
 | 
			
		||||
                  "_hash": 0.2687091190358901
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -8,20 +8,8 @@
 | 
			
		||||
    "configuration": null
 | 
			
		||||
  },
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "firstNodeIndex": 3,
 | 
			
		||||
    "firstNodeIndex": 6,
 | 
			
		||||
    "nodes": [
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 1069,
 | 
			
		||||
          "layoutY": 267
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
 | 
			
		||||
        "name": "Is Thermostat?",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "jsScript": "return msg.id.entityType === \"DEVICE\" && msg.type === \"thermostat\";"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 824,
 | 
			
		||||
@ -61,8 +49,8 @@
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 839,
 | 
			
		||||
          "layoutY": 345
 | 
			
		||||
          "layoutX": 825,
 | 
			
		||||
          "layoutY": 266
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbLogNode",
 | 
			
		||||
        "name": "Log RPC from Device",
 | 
			
		||||
@ -73,8 +61,8 @@
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 832,
 | 
			
		||||
          "layoutY": 407
 | 
			
		||||
          "layoutX": 825,
 | 
			
		||||
          "layoutY": 379
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbLogNode",
 | 
			
		||||
        "name": "Log Other",
 | 
			
		||||
@ -97,93 +85,51 @@
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 1069,
 | 
			
		||||
          "layoutY": 90
 | 
			
		||||
          "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
 | 
			
		||||
          "layoutX": 204,
 | 
			
		||||
          "layoutY": 240
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
 | 
			
		||||
        "name": "Is Thermostat?",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
 | 
			
		||||
        "name": "Device Profile Node",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "jsScript": "return metadata[\"deviceType\"] === \"thermostat\";"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 1090,
 | 
			
		||||
          "layoutY": 360
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbCreateRelationNode",
 | 
			
		||||
        "name": "Relate to Asset",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "direction": "FROM",
 | 
			
		||||
          "relationType": "ToAlarmPropagationAsset",
 | 
			
		||||
          "entityType": "ASSET",
 | 
			
		||||
          "entityNamePattern": "Thermostat Alarms",
 | 
			
		||||
          "entityTypePattern": "AlarmPropagationAsset",
 | 
			
		||||
          "entityCacheExpiration": 300,
 | 
			
		||||
          "createEntityIfNotExists": true,
 | 
			
		||||
          "changeOriginatorToRelatedEntity": false,
 | 
			
		||||
          "removeCurrentRelations": false
 | 
			
		||||
          "persistAlarmRulesState": false,
 | 
			
		||||
          "fetchAlarmRulesStateOnStart": false
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "connections": [
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 0,
 | 
			
		||||
        "toIndex": 8,
 | 
			
		||||
        "type": "True"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 1,
 | 
			
		||||
        "toIndex": 7,
 | 
			
		||||
        "fromIndex": 6,
 | 
			
		||||
        "toIndex": 2,
 | 
			
		||||
        "type": "Success"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 3,
 | 
			
		||||
        "toIndex": 5,
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 4,
 | 
			
		||||
        "type": "Other"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 3,
 | 
			
		||||
        "toIndex": 2,
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 1,
 | 
			
		||||
        "type": "Post attributes"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 3,
 | 
			
		||||
        "toIndex": 1,
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 0,
 | 
			
		||||
        "type": "Post telemetry"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 3,
 | 
			
		||||
        "toIndex": 4,
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 3,
 | 
			
		||||
        "type": "RPC Request from Device"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 3,
 | 
			
		||||
        "toIndex": 6,
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 5,
 | 
			
		||||
        "type": "RPC Request to Device"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 3,
 | 
			
		||||
        "toIndex": 0,
 | 
			
		||||
        "type": "Entity Created"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "ruleChainConnections": [
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 7,
 | 
			
		||||
        "targetRuleChainId": {
 | 
			
		||||
          "entityType": "RULE_CHAIN",
 | 
			
		||||
          "id": "25e26570-89ed-11ea-a650-cd6e14e633bd"
 | 
			
		||||
        },
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 1109,
 | 
			
		||||
          "layoutY": 182,
 | 
			
		||||
          "ruleChainNodeId": "rule-chain-node-10"
 | 
			
		||||
        },
 | 
			
		||||
        "type": "True"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
    "ruleChainConnections": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,138 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "ruleChain": {
 | 
			
		||||
    "additionalInfo": {
 | 
			
		||||
      "description": ""
 | 
			
		||||
    },
 | 
			
		||||
    "name": "Thermostat Alarms",
 | 
			
		||||
    "firstRuleNodeId": null,
 | 
			
		||||
    "root": false,
 | 
			
		||||
    "debugMode": false,
 | 
			
		||||
    "configuration": null
 | 
			
		||||
  },
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "firstNodeIndex": 6,
 | 
			
		||||
    "nodes": [
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 822,
 | 
			
		||||
          "layoutY": 294
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
 | 
			
		||||
        "name": "Save Timeseries",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "defaultTTL": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "description": null,
 | 
			
		||||
          "layoutX": 824,
 | 
			
		||||
          "layoutY": 221
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
 | 
			
		||||
        "name": "Save Client Attributes",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "scope": "SERVER_SCOPE",
 | 
			
		||||
          "notifyDevice": null
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 494,
 | 
			
		||||
          "layoutY": 309
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
 | 
			
		||||
        "name": "Message Type Switch",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "version": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 824,
 | 
			
		||||
          "layoutY": 383
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbLogNode",
 | 
			
		||||
        "name": "Log RPC from Device",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 823,
 | 
			
		||||
          "layoutY": 444
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbLogNode",
 | 
			
		||||
        "name": "Log Other",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 822,
 | 
			
		||||
          "layoutY": 507
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
 | 
			
		||||
        "name": "RPC Call Request",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "timeoutInSeconds": 60
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "description": "",
 | 
			
		||||
          "layoutX": 209,
 | 
			
		||||
          "layoutY": 307
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
 | 
			
		||||
        "name": "Device Profile Node",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "persistAlarmRulesState": false,
 | 
			
		||||
          "fetchAlarmRulesStateOnStart": false
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "connections": [
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 4,
 | 
			
		||||
        "type": "Other"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 1,
 | 
			
		||||
        "type": "Post attributes"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 0,
 | 
			
		||||
        "type": "Post telemetry"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 3,
 | 
			
		||||
        "type": "RPC Request from Device"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "toIndex": 5,
 | 
			
		||||
        "type": "RPC Request to Device"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 6,
 | 
			
		||||
        "toIndex": 2,
 | 
			
		||||
        "type": "Success"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "ruleChainConnections": null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -18,9 +18,7 @@ package org.thingsboard.server.service.device;
 | 
			
		||||
import com.fasterxml.jackson.core.JsonProcessingException;
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import com.google.common.util.concurrent.MoreExecutors;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.apache.commons.lang.RandomStringUtils;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
@ -114,6 +112,13 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
 | 
			
		||||
    public ProvisionResponse provisionDevice(ProvisionRequest provisionRequest) {
 | 
			
		||||
        String provisionRequestKey = provisionRequest.getCredentials().getProvisionDeviceKey();
 | 
			
		||||
        String provisionRequestSecret = provisionRequest.getCredentials().getProvisionDeviceSecret();
 | 
			
		||||
        if (provisionRequest.getDeviceName() != null) {
 | 
			
		||||
            provisionRequest.setDeviceName(provisionRequest.getDeviceName().trim());
 | 
			
		||||
            if (StringUtils.isEmpty(provisionRequest.getDeviceName())) {
 | 
			
		||||
                log.warn("Provision request contains empty device name!");
 | 
			
		||||
                throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (StringUtils.isEmpty(provisionRequestKey) || StringUtils.isEmpty(provisionRequestSecret)) {
 | 
			
		||||
            throw new ProvisionFailedException(ProvisionResponseStatus.NOT_FOUND.name());
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,6 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.LongDataEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.page.PageLink;
 | 
			
		||||
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
 | 
			
		||||
import org.thingsboard.server.common.data.query.DynamicValue;
 | 
			
		||||
import org.thingsboard.server.common.data.query.DynamicValueSourceType;
 | 
			
		||||
@ -73,7 +72,6 @@ import org.thingsboard.server.dao.device.DeviceCredentialsService;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceProfileService;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceService;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.rule.RuleChainService;
 | 
			
		||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
 | 
			
		||||
import org.thingsboard.server.dao.tenant.TenantService;
 | 
			
		||||
@ -129,9 +127,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private DeviceCredentialsService deviceCredentialsService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private RuleChainService ruleChainService;
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    protected BCryptPasswordEncoder passwordEncoder() {
 | 
			
		||||
        return new BCryptPasswordEncoder();
 | 
			
		||||
@ -271,8 +266,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        thermostatDeviceProfile.setTransportType(DeviceTransportType.DEFAULT);
 | 
			
		||||
        thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
 | 
			
		||||
        thermostatDeviceProfile.setDescription("Thermostat device profile");
 | 
			
		||||
        thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains(
 | 
			
		||||
                demoTenant.getId(), new PageLink(1, 0, "Thermostat Alarms")).getData().get(0).getId());
 | 
			
		||||
 | 
			
		||||
        DeviceProfileData deviceProfileData = new DeviceProfileData();
 | 
			
		||||
        DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
 | 
			
		||||
@ -290,13 +283,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        AlarmCondition temperatureCondition = new AlarmCondition();
 | 
			
		||||
        temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
 | 
			
		||||
 | 
			
		||||
        KeyFilter alarmTemperatureAttributeFilter = new KeyFilter();
 | 
			
		||||
        alarmTemperatureAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmTemperature"));
 | 
			
		||||
        alarmTemperatureAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
 | 
			
		||||
        BooleanFilterPredicate alarmTemperatureAttributePredicate = new BooleanFilterPredicate();
 | 
			
		||||
        alarmTemperatureAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
 | 
			
		||||
        alarmTemperatureAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
 | 
			
		||||
        alarmTemperatureAttributeFilter.setPredicate(alarmTemperatureAttributePredicate);
 | 
			
		||||
        KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter();
 | 
			
		||||
        temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag"));
 | 
			
		||||
        temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
 | 
			
		||||
        BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate();
 | 
			
		||||
        temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
 | 
			
		||||
        temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
 | 
			
		||||
        temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate);
 | 
			
		||||
 | 
			
		||||
        KeyFilter temperatureTimeseriesFilter = new KeyFilter();
 | 
			
		||||
        temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
 | 
			
		||||
@ -305,10 +298,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
 | 
			
		||||
        FilterPredicateValue<Double> temperatureTimeseriesPredicateValue =
 | 
			
		||||
                new FilterPredicateValue<>(25.0, null,
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature"));
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "temperatureAlarmThreshold"));
 | 
			
		||||
        temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue);
 | 
			
		||||
        temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate);
 | 
			
		||||
        temperatureCondition.setCondition(Arrays.asList(alarmTemperatureAttributeFilter, temperatureTimeseriesFilter));
 | 
			
		||||
        temperatureCondition.setCondition(Arrays.asList(temperatureAlarmFlagAttributeFilter, temperatureTimeseriesFilter));
 | 
			
		||||
        temperatureRule.setAlarmDetails("Current temperature = ${temperature}");
 | 
			
		||||
        temperatureRule.setCondition(temperatureCondition);
 | 
			
		||||
        highTemperature.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule)));
 | 
			
		||||
@ -324,7 +317,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
 | 
			
		||||
        FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue =
 | 
			
		||||
                new FilterPredicateValue<>(25.0, null,
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature"));
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "temperatureAlarmThreshold"));
 | 
			
		||||
 | 
			
		||||
        clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue);
 | 
			
		||||
        clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate);
 | 
			
		||||
@ -340,13 +333,13 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        AlarmCondition humidityCondition = new AlarmCondition();
 | 
			
		||||
        humidityCondition.setSpec(new SimpleAlarmConditionSpec());
 | 
			
		||||
 | 
			
		||||
        KeyFilter alarmHumidityAttributeFilter = new KeyFilter();
 | 
			
		||||
        alarmHumidityAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmHumidity"));
 | 
			
		||||
        alarmHumidityAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
 | 
			
		||||
        BooleanFilterPredicate alarmHumidityAttributePredicate = new BooleanFilterPredicate();
 | 
			
		||||
        alarmHumidityAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
 | 
			
		||||
        alarmHumidityAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
 | 
			
		||||
        alarmHumidityAttributeFilter.setPredicate(alarmHumidityAttributePredicate);
 | 
			
		||||
        KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter();
 | 
			
		||||
        humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag"));
 | 
			
		||||
        humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
 | 
			
		||||
        BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate();
 | 
			
		||||
        humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
 | 
			
		||||
        humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
 | 
			
		||||
        humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate);
 | 
			
		||||
 | 
			
		||||
        KeyFilter humidityTimeseriesFilter = new KeyFilter();
 | 
			
		||||
        humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
 | 
			
		||||
@ -355,10 +348,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
 | 
			
		||||
        FilterPredicateValue<Double> humidityTimeseriesPredicateValue =
 | 
			
		||||
                new FilterPredicateValue<>(60.0, null,
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity"));
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "humidityAlarmThreshold"));
 | 
			
		||||
        humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue);
 | 
			
		||||
        humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate);
 | 
			
		||||
        humidityCondition.setCondition(Arrays.asList(alarmHumidityAttributeFilter, humidityTimeseriesFilter));
 | 
			
		||||
        humidityCondition.setCondition(Arrays.asList(humidityAlarmFlagAttributeFilter, humidityTimeseriesFilter));
 | 
			
		||||
 | 
			
		||||
        humidityRule.setCondition(humidityCondition);
 | 
			
		||||
        humidityRule.setAlarmDetails("Current humidity = ${humidity}");
 | 
			
		||||
@ -375,7 +368,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
 | 
			
		||||
        FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue =
 | 
			
		||||
                new FilterPredicateValue<>(60.0, null,
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity"));
 | 
			
		||||
                        new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "humidityAlarmThreshold"));
 | 
			
		||||
 | 
			
		||||
        clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue);
 | 
			
		||||
        clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate);
 | 
			
		||||
@ -394,18 +387,18 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
        attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE,
 | 
			
		||||
                Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -122.1503)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmTemperature", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmHumidity", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdTemperature", (long) 20)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdHumidity", (long) 50))));
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("humidityAlarmFlag", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperatureAlarmThreshold", (long) 20)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 50))));
 | 
			
		||||
 | 
			
		||||
        attributesService.save(demoTenant.getId(), t2Id, DataConstants.SERVER_SCOPE,
 | 
			
		||||
                Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.493801)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("longitude", -121.948769)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmTemperature", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("alarmHumidity", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdTemperature", (long) 25)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("thresholdHumidity", (long) 30))));
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("temperatureAlarmFlag", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("humidityAlarmFlag", true)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperatureAlarmThreshold", (long) 25)),
 | 
			
		||||
                        new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 30))));
 | 
			
		||||
 | 
			
		||||
        installScripts.loadDashboards(demoTenant.getId(), null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -212,20 +212,10 @@ public class InstallScripts {
 | 
			
		||||
    public void loadDemoRuleChains(TenantId tenantId) throws Exception {
 | 
			
		||||
        Path ruleChainsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, RULE_CHAINS_DIR);
 | 
			
		||||
        try {
 | 
			
		||||
            JsonNode ruleChainJson = objectMapper.readTree(ruleChainsDir.resolve("thermostat_alarms.json").toFile());
 | 
			
		||||
            RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
 | 
			
		||||
            RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
 | 
			
		||||
            ruleChain.setTenantId(tenantId);
 | 
			
		||||
            ruleChain = ruleChainService.saveRuleChain(ruleChain);
 | 
			
		||||
            ruleChainMetaData.setRuleChainId(ruleChain.getId());
 | 
			
		||||
            ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData);
 | 
			
		||||
 | 
			
		||||
            JsonNode rootChainJson = objectMapper.readTree(ruleChainsDir.resolve("root_rule_chain.json").toFile());
 | 
			
		||||
            RuleChain rootChain = objectMapper.treeToValue(rootChainJson.get("ruleChain"), RuleChain.class);
 | 
			
		||||
            RuleChainMetaData rootChainMetaData = objectMapper.treeToValue(rootChainJson.get("metadata"), RuleChainMetaData.class);
 | 
			
		||||
 | 
			
		||||
            RuleChainId thermostatsRuleChainId = ruleChain.getId();
 | 
			
		||||
            rootChainMetaData.getRuleChainConnections().forEach(connection -> connection.setTargetRuleChainId(thermostatsRuleChainId));
 | 
			
		||||
            rootChain.setTenantId(tenantId);
 | 
			
		||||
            rootChain = ruleChainService.saveRuleChain(rootChain);
 | 
			
		||||
            rootChainMetaData.setRuleChainId(rootChain.getId());
 | 
			
		||||
 | 
			
		||||
@ -15,9 +15,6 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.device;
 | 
			
		||||
 | 
			
		||||
import com.google.common.util.concurrent.ListenableFuture;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.DeviceProfile;
 | 
			
		||||
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
 | 
			
		||||
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
 | 
			
		||||
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
 | 
			
		||||
 | 
			
		||||
@ -179,6 +179,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ctx.close();
 | 
			
		||||
                        throw new RuntimeException("Unsupported topic for provisioning requests!");
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (RuntimeException | AdaptorException e) {
 | 
			
		||||
 | 
			
		||||
@ -54,7 +54,8 @@ import java.util.concurrent.TimeUnit;
 | 
			
		||||
        relationTypes = {"Alarm Created", "Alarm Updated", "Alarm Severity Updated", "Alarm Cleared", "Success", "Failure"},
 | 
			
		||||
        configClazz = TbDeviceProfileNodeConfiguration.class,
 | 
			
		||||
        nodeDescription = "Process device messages based on device profile settings",
 | 
			
		||||
        nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. Generates ",
 | 
			
		||||
        nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. The output relation type is either " +
 | 
			
		||||
                "'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.",
 | 
			
		||||
        uiResources = {"static/rulenode/rulenode-core-config.js"},
 | 
			
		||||
        configDirective = "tbDeviceProfileConfig"
 | 
			
		||||
)
 | 
			
		||||
@ -119,7 +120,6 @@ public class TbDeviceProfileNode implements TbNode {
 | 
			
		||||
            } else {
 | 
			
		||||
                removeDeviceState(deviceId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            if (EntityType.DEVICE.equals(originatorType)) {
 | 
			
		||||
                DeviceId deviceId = new DeviceId(msg.getOriginator().getId());
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,9 @@ export enum ActionType {
 | 
			
		||||
  LOGOUT = 'LOGOUT',
 | 
			
		||||
  LOCKOUT = 'LOCKOUT',
 | 
			
		||||
  ASSIGNED_FROM_TENANT = 'ASSIGNED_FROM_TENANT',
 | 
			
		||||
  ASSIGNED_TO_TENANT = 'ASSIGNED_TO_TENANT'
 | 
			
		||||
  ASSIGNED_TO_TENANT = 'ASSIGNED_TO_TENANT',
 | 
			
		||||
  PROVISION_SUCCESS = 'PROVISION_SUCCESS',
 | 
			
		||||
  PROVISION_FAILURE = 'PROVISION_FAILURE'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum ActionStatus {
 | 
			
		||||
@ -83,7 +85,9 @@ export const actionTypeTranslations = new Map<ActionType, string>(
 | 
			
		||||
    [ActionType.LOGOUT, 'audit-log.type-logout'],
 | 
			
		||||
    [ActionType.LOCKOUT, 'audit-log.type-lockout'],
 | 
			
		||||
    [ActionType.ASSIGNED_FROM_TENANT, 'audit-log.type-assigned-from-tenant'],
 | 
			
		||||
    [ActionType.ASSIGNED_TO_TENANT, 'audit-log.type-assigned-to-tenant']
 | 
			
		||||
    [ActionType.ASSIGNED_TO_TENANT, 'audit-log.type-assigned-to-tenant'],
 | 
			
		||||
    [ActionType.PROVISION_SUCCESS, 'audit-log.type-provision-success'],
 | 
			
		||||
    [ActionType.PROVISION_FAILURE, 'audit-log.type-provision-failure']
 | 
			
		||||
  ]
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -472,7 +472,9 @@
 | 
			
		||||
        "search": "Search audit logs",
 | 
			
		||||
        "clear-search": "Clear search",
 | 
			
		||||
        "type-assigned-from-tenant": "Assigned from Tenant",
 | 
			
		||||
        "type-assigned-to-tenant": "Assigned to Tenant"
 | 
			
		||||
        "type-assigned-to-tenant": "Assigned to Tenant",
 | 
			
		||||
        "type-provision-success": "Device provisioned",
 | 
			
		||||
        "type-provision-failure": "Device provisioning was failed"
 | 
			
		||||
    },
 | 
			
		||||
    "confirm-on-exit": {
 | 
			
		||||
        "message": "You have unsaved changes. Are you sure you want to leave this page?",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user